@flutchai/flutch-sdk 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -9,6 +9,7 @@ import { Registry } from 'prom-client';
9
9
  import { Response, Request } from 'express';
10
10
  import { DiscoveryService, MetadataScanner, ModuleRef } from '@nestjs/core';
11
11
  import { StructuredTool } from '@langchain/core/tools';
12
+ import { RunnableConfig } from '@langchain/core/runnables';
12
13
  import { ChatAnthropic } from '@langchain/anthropic';
13
14
  import { ChatCohere } from '@langchain/cohere';
14
15
  import { ChatMistralAI } from '@langchain/mistralai';
@@ -1135,18 +1136,6 @@ declare class EventProcessor {
1135
1136
  processEvent(acc: StreamAccumulator, event: any, onPartial?: (chunk: string) => void): void;
1136
1137
  getResult(acc: StreamAccumulator): {
1137
1138
  content: IStoredMessageContent;
1138
- metrics: {
1139
- modelCalls: Array<{
1140
- nodeName: string;
1141
- timestamp: number;
1142
- modelId: string;
1143
- promptTokens: number;
1144
- completionTokens: number;
1145
- totalTokens: number;
1146
- latencyMs: number;
1147
- }>;
1148
- apiCalls: any[];
1149
- } | null;
1150
1139
  trace: {
1151
1140
  events: IGraphTraceEvent[];
1152
1141
  startedAt: number;
@@ -1418,7 +1407,6 @@ declare class McpConverter {
1418
1407
  private readonly mcpRuntimeUrl;
1419
1408
  constructor(mcpRuntimeUrl?: string);
1420
1409
  private jsonSchemaToZod;
1421
- private mapPrimitiveSchema;
1422
1410
  convertTool(mcpTool: McpTool): LangChainStructuredTool;
1423
1411
  convertTools(mcpTools: McpTool[]): Promise<LangChainStructuredTool[]>;
1424
1412
  fetchAndConvertTools(filter?: string): Promise<LangChainStructuredTool[]>;
@@ -1445,6 +1433,10 @@ declare class McpRuntimeHttpClient implements McpRuntimeClient {
1445
1433
  executeTool(name: string, args: any, context?: any): Promise<ToolExecutionResult>;
1446
1434
  getToolStats(): Promise<any>;
1447
1435
  isHealthy(): Promise<boolean>;
1436
+ executeToolWithEvents(toolCallId: string, toolName: string, enrichedArgs: Record<string, any>, executionContext: Record<string, any>, config?: RunnableConfig): Promise<{
1437
+ content: string;
1438
+ success: boolean;
1439
+ }>;
1448
1440
  }
1449
1441
 
1450
1442
  declare enum RetrieverSearchType {
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ import { Registry } from 'prom-client';
9
9
  import { Response, Request } from 'express';
10
10
  import { DiscoveryService, MetadataScanner, ModuleRef } from '@nestjs/core';
11
11
  import { StructuredTool } from '@langchain/core/tools';
12
+ import { RunnableConfig } from '@langchain/core/runnables';
12
13
  import { ChatAnthropic } from '@langchain/anthropic';
13
14
  import { ChatCohere } from '@langchain/cohere';
14
15
  import { ChatMistralAI } from '@langchain/mistralai';
@@ -1135,18 +1136,6 @@ declare class EventProcessor {
1135
1136
  processEvent(acc: StreamAccumulator, event: any, onPartial?: (chunk: string) => void): void;
1136
1137
  getResult(acc: StreamAccumulator): {
1137
1138
  content: IStoredMessageContent;
1138
- metrics: {
1139
- modelCalls: Array<{
1140
- nodeName: string;
1141
- timestamp: number;
1142
- modelId: string;
1143
- promptTokens: number;
1144
- completionTokens: number;
1145
- totalTokens: number;
1146
- latencyMs: number;
1147
- }>;
1148
- apiCalls: any[];
1149
- } | null;
1150
1139
  trace: {
1151
1140
  events: IGraphTraceEvent[];
1152
1141
  startedAt: number;
@@ -1418,7 +1407,6 @@ declare class McpConverter {
1418
1407
  private readonly mcpRuntimeUrl;
1419
1408
  constructor(mcpRuntimeUrl?: string);
1420
1409
  private jsonSchemaToZod;
1421
- private mapPrimitiveSchema;
1422
1410
  convertTool(mcpTool: McpTool): LangChainStructuredTool;
1423
1411
  convertTools(mcpTools: McpTool[]): Promise<LangChainStructuredTool[]>;
1424
1412
  fetchAndConvertTools(filter?: string): Promise<LangChainStructuredTool[]>;
@@ -1445,6 +1433,10 @@ declare class McpRuntimeHttpClient implements McpRuntimeClient {
1445
1433
  executeTool(name: string, args: any, context?: any): Promise<ToolExecutionResult>;
1446
1434
  getToolStats(): Promise<any>;
1447
1435
  isHealthy(): Promise<boolean>;
1436
+ executeToolWithEvents(toolCallId: string, toolName: string, enrichedArgs: Record<string, any>, executionContext: Record<string, any>, config?: RunnableConfig): Promise<{
1437
+ content: string;
1438
+ success: boolean;
1439
+ }>;
1448
1440
  }
1449
1441
 
1450
1442
  declare enum RetrieverSearchType {
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import * as LangGraph from '@langchain/langgraph';
13
13
  import { DynamicStructuredTool } from '@langchain/core/tools';
14
14
  import { z } from 'zod';
15
15
  import axios2 from 'axios';
16
+ import { parseCallbackConfigArg, CallbackManager } from '@langchain/core/callbacks/manager';
16
17
  import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
17
18
  import { AzureChatOpenAI } from '@langchain/azure-openai';
18
19
  import { ChatAnthropic } from '@langchain/anthropic';
@@ -4301,7 +4302,7 @@ function sanitizeTraceData(value, depth = 0, seen = /* @__PURE__ */ new WeakSet(
4301
4302
  return null;
4302
4303
  }
4303
4304
  if (typeof value === "string") {
4304
- return value.length > opts.maxStringLength ? `${value.slice(0, opts.maxStringLength)}\u2026` : value;
4305
+ return value;
4305
4306
  }
4306
4307
  if (typeof value === "number" || typeof value === "boolean") {
4307
4308
  return value;
@@ -4557,6 +4558,31 @@ var EventProcessor = class {
4557
4558
  }
4558
4559
  return;
4559
4560
  }
4561
+ if (event.event === "on_tool_start") {
4562
+ this.logger.log("\u{1F527} Tool execution started", {
4563
+ toolName: event.name,
4564
+ input: event.data?.input,
4565
+ runId: event.run_id,
4566
+ metadata: event.metadata
4567
+ });
4568
+ return;
4569
+ }
4570
+ if (event.event === "on_tool_end") {
4571
+ this.logger.log("\u2705 Tool execution completed", {
4572
+ toolName: event.name,
4573
+ output: typeof event.data?.output === "string" ? event.data.output.substring(0, 200) + (event.data.output.length > 200 ? "..." : "") : event.data?.output,
4574
+ runId: event.run_id
4575
+ });
4576
+ return;
4577
+ }
4578
+ if (event.event === "on_tool_error") {
4579
+ this.logger.error("\u274C Tool execution failed", {
4580
+ toolName: event.name,
4581
+ error: event.data?.error,
4582
+ runId: event.run_id
4583
+ });
4584
+ return;
4585
+ }
4560
4586
  if (event.event === "on_chat_model_end") {
4561
4587
  const output = event.data?.output;
4562
4588
  const usageMetadata = output?.usage_metadata || output?.usageMetadata;
@@ -4682,42 +4708,9 @@ var EventProcessor = class {
4682
4708
  /**
4683
4709
  * Build final result from accumulator
4684
4710
  * Uses generation if available, otherwise falls back to streamed text
4685
- * Returns content and metrics separately (metrics should NOT be stored in message.metadata)
4711
+ * Returns content and trace events (metrics should be extracted from trace on backend)
4686
4712
  */
4687
4713
  getResult(acc) {
4688
- const totalPromptTokens = acc.llmCalls.reduce(
4689
- (sum, call) => sum + call.promptTokens,
4690
- 0
4691
- );
4692
- const totalCompletionTokens = acc.llmCalls.reduce(
4693
- (sum, call) => sum + call.completionTokens,
4694
- 0
4695
- );
4696
- const totalTokens = acc.llmCalls.reduce(
4697
- (sum, call) => sum + call.totalTokens,
4698
- 0
4699
- );
4700
- this.logger.log("\u{1F4CA} Final metrics collected", {
4701
- llmCallsCount: acc.llmCalls.length,
4702
- totalPromptTokens,
4703
- totalCompletionTokens,
4704
- totalTokens,
4705
- modelIds: acc.llmCalls.map((c) => c.modelId)
4706
- });
4707
- const metrics = acc.llmCalls.length > 0 ? {
4708
- modelCalls: acc.llmCalls.map((call) => ({
4709
- nodeName: call.nodeName || "unknown",
4710
- timestamp: call.timestamp,
4711
- modelId: call.modelId,
4712
- promptTokens: call.promptTokens,
4713
- completionTokens: call.completionTokens,
4714
- totalTokens: call.totalTokens,
4715
- latencyMs: 0
4716
- // Not calculated from events
4717
- })),
4718
- apiCalls: []
4719
- // TODO: Add API calls tracking (rerank, embeddings) via custom events
4720
- } : null;
4721
4714
  const startedAt = acc.traceStartedAt ?? Date.now();
4722
4715
  const completedAt = acc.traceCompletedAt ?? startedAt;
4723
4716
  const trace = acc.traceEvents.length > 0 ? {
@@ -4746,7 +4739,6 @@ var EventProcessor = class {
4746
4739
  metadata: acc.generation?.metadata || {},
4747
4740
  reasoningChains: acc.reasoningChains.length > 0 ? acc.reasoningChains : void 0
4748
4741
  },
4749
- metrics,
4750
4742
  trace
4751
4743
  };
4752
4744
  }
@@ -4861,33 +4853,14 @@ var LangGraphEngine = class {
4861
4853
  );
4862
4854
  }
4863
4855
  }
4864
- const { content, metrics, trace } = this.eventProcessor.getResult(acc);
4856
+ const { content, trace } = this.eventProcessor.getResult(acc);
4865
4857
  this.logger.debug("[STREAM-RESULT] Got result from EventProcessor", {
4866
4858
  hasContent: !!content,
4867
- hasMetrics: !!metrics,
4868
- modelCallsCount: metrics?.modelCalls?.length || 0,
4869
- apiCallsCount: metrics?.apiCalls?.length || 0,
4870
4859
  hasContext: !!config.configurable?.context,
4871
4860
  hasTrace: !!trace,
4872
- traceEvents: trace?.events?.length || 0
4861
+ traceEvents: trace?.events?.length || 0,
4862
+ totalModelCalls: trace?.totalModelCalls || 0
4873
4863
  });
4874
- if (metrics && metrics.modelCalls?.length > 0 && config.configurable?.context) {
4875
- const context = config.configurable.context;
4876
- await this.sendMetricsWebhook({
4877
- messageId: context.messageId || "unknown",
4878
- threadId: context.threadId || "unknown",
4879
- userId: context.userId || "unknown",
4880
- agentId: context.agentId || "unknown",
4881
- companyId: context.companyId || "unknown",
4882
- metrics
4883
- });
4884
- } else {
4885
- this.logger.debug("[METRICS-WEBHOOK] Skipping webhook", {
4886
- hasMetrics: !!metrics,
4887
- modelCallsCount: metrics?.modelCalls?.length || 0,
4888
- hasContext: !!config.configurable?.context
4889
- });
4890
- }
4891
4864
  if (trace && trace.events.length > 0 && config.configurable?.context) {
4892
4865
  const context = config.configurable.context;
4893
4866
  this.logger.debug("[TRACE-WEBHOOK] Sending trace events batch", {
@@ -5367,47 +5340,65 @@ var McpConverter = class _McpConverter {
5367
5340
  mcpRuntimeUrl;
5368
5341
  constructor(mcpRuntimeUrl = "http://localhost:3004") {
5369
5342
  this.mcpRuntimeUrl = mcpRuntimeUrl;
5343
+ this.logger.log(
5344
+ `\u{1F527} McpConverter initialized with SDK version 0.1.8 (manual jsonSchemaToZod)`
5345
+ );
5370
5346
  }
5371
5347
  /**
5372
- * Convert JSON Schema to simplified Zod schema for LangChain
5348
+ * Convert JSON Schema to Zod schema manually
5349
+ * This creates a standard Zod schema that zodToJsonSchema can convert back properly
5373
5350
  */
5374
5351
  jsonSchemaToZod(jsonSchema) {
5375
5352
  if (!jsonSchema || typeof jsonSchema !== "object") {
5376
5353
  return z.any();
5377
5354
  }
5378
- if (jsonSchema.type !== "object") {
5379
- return this.mapPrimitiveSchema(jsonSchema.type, true);
5380
- }
5381
- const properties = jsonSchema.properties || {};
5382
- const required = Array.isArray(jsonSchema.required) ? jsonSchema.required : [];
5383
- const zodShape = {};
5384
- for (const [key, prop] of Object.entries(properties)) {
5385
- const propDef = prop;
5386
- const isRequired = required.includes(key);
5387
- const schemaType = propDef?.type ?? "any";
5388
- const mappedType = this.mapPrimitiveSchema(schemaType, isRequired);
5389
- zodShape[key] = mappedType;
5390
- }
5391
- const baseObject = z.object(zodShape);
5392
- const configuredObject = jsonSchema.additionalProperties === false ? baseObject.strict() : baseObject.passthrough();
5393
- return configuredObject;
5394
- }
5395
- mapPrimitiveSchema(type, required) {
5396
- const optionalize = (schema) => required ? schema : schema.optional();
5397
- switch (type) {
5398
- case "string":
5399
- return optionalize(z.string());
5400
- case "number":
5401
- case "integer":
5402
- return optionalize(z.number());
5403
- case "boolean":
5404
- return optionalize(z.boolean());
5405
- case "array":
5406
- return optionalize(z.array(z.any()));
5407
- case "object":
5408
- return optionalize(z.record(z.any()));
5409
- default:
5410
- return optionalize(z.any());
5355
+ try {
5356
+ if (jsonSchema.type === "object" && jsonSchema.properties) {
5357
+ const shape = {};
5358
+ for (const [key, propSchema] of Object.entries(jsonSchema.properties)) {
5359
+ const prop = propSchema;
5360
+ let zodProp;
5361
+ switch (prop.type) {
5362
+ case "string":
5363
+ zodProp = z.string();
5364
+ break;
5365
+ case "number":
5366
+ zodProp = z.number();
5367
+ break;
5368
+ case "boolean":
5369
+ zodProp = z.boolean();
5370
+ break;
5371
+ case "integer":
5372
+ zodProp = z.number().int();
5373
+ break;
5374
+ case "array":
5375
+ zodProp = z.array(z.any());
5376
+ break;
5377
+ case "object":
5378
+ zodProp = z.record(z.any());
5379
+ break;
5380
+ default:
5381
+ zodProp = z.any();
5382
+ }
5383
+ if (prop.description) {
5384
+ zodProp = zodProp.describe(prop.description);
5385
+ }
5386
+ if (!jsonSchema.required?.includes(key)) {
5387
+ zodProp = zodProp.optional();
5388
+ }
5389
+ shape[key] = zodProp;
5390
+ }
5391
+ return z.object(shape);
5392
+ }
5393
+ this.logger.warn(
5394
+ `Unsupported JSON Schema structure, falling back to z.any()`
5395
+ );
5396
+ return z.any();
5397
+ } catch (error) {
5398
+ this.logger.warn(
5399
+ `Failed to convert JSON Schema, falling back to z.any(): ${error}`
5400
+ );
5401
+ return z.any();
5411
5402
  }
5412
5403
  }
5413
5404
  /**
@@ -5416,6 +5407,27 @@ var McpConverter = class _McpConverter {
5416
5407
  convertTool(mcpTool) {
5417
5408
  const logger2 = this.logger;
5418
5409
  const mcpRuntimeUrl = this.mcpRuntimeUrl;
5410
+ let enhancedDescription = mcpTool.description;
5411
+ if (mcpTool.inputSchema?.properties) {
5412
+ const paramDescriptions = [];
5413
+ for (const [key, propSchema] of Object.entries(
5414
+ mcpTool.inputSchema.properties
5415
+ )) {
5416
+ const prop = propSchema;
5417
+ if (prop.description) {
5418
+ const isRequired = mcpTool.inputSchema.required?.includes(key);
5419
+ paramDescriptions.push(
5420
+ `- ${key}${isRequired ? " (required)" : ""}: ${prop.description}`
5421
+ );
5422
+ }
5423
+ }
5424
+ if (paramDescriptions.length > 0) {
5425
+ enhancedDescription = `${mcpTool.description}
5426
+
5427
+ Parameters:
5428
+ ${paramDescriptions.join("\n")}`;
5429
+ }
5430
+ }
5419
5431
  const schema = this.jsonSchemaToZod(mcpTool.inputSchema);
5420
5432
  logger2.debug(
5421
5433
  `\u{1F527} [${mcpTool.name}] Original schema:`,
@@ -5424,9 +5436,48 @@ var McpConverter = class _McpConverter {
5424
5436
  logger2.debug(
5425
5437
  `\u{1F527} [${mcpTool.name}] Using schema type: ${schema?._def?.typeName ?? "unknown"}`
5426
5438
  );
5439
+ if (schema?._def?.shape && typeof schema._def.shape === "function") {
5440
+ try {
5441
+ const shape = schema._def.shape();
5442
+ logger2.debug(
5443
+ `\u{1F527} [${mcpTool.name}] Converted Zod schema shape:`,
5444
+ JSON.stringify(
5445
+ Object.entries(shape).reduce(
5446
+ (acc, [key, val]) => {
5447
+ acc[key] = {
5448
+ type: val?._def?.typeName,
5449
+ description: val?._def?.description,
5450
+ optional: val?._def?.typeName === "ZodOptional"
5451
+ };
5452
+ return acc;
5453
+ },
5454
+ {}
5455
+ ),
5456
+ null,
5457
+ 2
5458
+ )
5459
+ );
5460
+ } catch (error) {
5461
+ logger2.debug(
5462
+ `\u{1F527} [${mcpTool.name}] Could not extract Zod schema shape: ${error}`
5463
+ );
5464
+ }
5465
+ }
5466
+ try {
5467
+ const { zodToJsonSchema } = __require("zod-to-json-schema");
5468
+ const convertedJsonSchema = zodToJsonSchema(schema);
5469
+ logger2.warn(
5470
+ `\u{1F527} [${mcpTool.name}] JSON Schema that LangChain will use:`,
5471
+ JSON.stringify(convertedJsonSchema, null, 2)
5472
+ );
5473
+ } catch (error) {
5474
+ logger2.warn(
5475
+ `\u{1F527} [${mcpTool.name}] Could not convert Zod to JSON Schema: ${error}`
5476
+ );
5477
+ }
5427
5478
  return new DynamicStructuredTool({
5428
5479
  name: mcpTool.name,
5429
- description: mcpTool.description,
5480
+ description: enhancedDescription,
5430
5481
  schema,
5431
5482
  func: async (input) => {
5432
5483
  logger2.log(`\u{1F527} [${mcpTool.name}] LLM INPUT: ${JSON.stringify(input)}`);
@@ -5743,6 +5794,58 @@ var McpRuntimeHttpClient = class {
5743
5794
  return false;
5744
5795
  }
5745
5796
  }
5797
+ /**
5798
+ * Execute tool with LangChain event emission
5799
+ * @param toolCallId - Tool call ID from LLM
5800
+ * @param toolName - Tool name
5801
+ * @param enrichedArgs - Merged arguments (toolConfig + LLM args)
5802
+ * @param executionContext - Execution context (userId, agentId, etc.)
5803
+ * @param config - RunnableConfig with callbacks
5804
+ * @returns Tool execution result with content
5805
+ */
5806
+ async executeToolWithEvents(toolCallId, toolName, enrichedArgs, executionContext, config) {
5807
+ const parsedConfig = parseCallbackConfigArg(config);
5808
+ const callbackManager = CallbackManager.configure(parsedConfig.callbacks);
5809
+ let runManager;
5810
+ try {
5811
+ runManager = await callbackManager?.handleToolStart(
5812
+ {
5813
+ name: toolName,
5814
+ lc: 1,
5815
+ type: "not_implemented",
5816
+ id: ["langchain", "tools", "mcp", toolName]
5817
+ },
5818
+ JSON.stringify(enrichedArgs),
5819
+ parsedConfig.runId,
5820
+ void 0,
5821
+ parsedConfig.tags,
5822
+ parsedConfig.metadata,
5823
+ toolName
5824
+ );
5825
+ const result = await this.executeTool(
5826
+ toolName,
5827
+ enrichedArgs,
5828
+ executionContext
5829
+ );
5830
+ const content = result.success ? JSON.stringify(result) : result.error || JSON.stringify(result);
5831
+ await runManager?.handleToolEnd(content);
5832
+ return {
5833
+ content,
5834
+ success: result.success
5835
+ };
5836
+ } catch (error) {
5837
+ this.logger.error(`Error executing tool ${toolName}:`, error);
5838
+ await runManager?.handleToolError(error);
5839
+ const errorContent = JSON.stringify({
5840
+ success: false,
5841
+ error: error instanceof Error ? error.message : "Tool execution failed"
5842
+ });
5843
+ return {
5844
+ content: errorContent,
5845
+ success: false
5846
+ };
5847
+ }
5848
+ }
5746
5849
  };
5747
5850
  McpRuntimeHttpClient = __decorateClass([
5748
5851
  Injectable()