echo-ai-sdk-ts 2.3.1 → 2.4.0
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/README.md +35 -0
- package/dist/index.d.mts +51 -2
- package/dist/index.d.ts +51 -2
- package/dist/index.js +155 -13
- package/dist/index.mjs +152 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -113,6 +113,41 @@ bot.use(async ({ sessionId, message }) => {
|
|
|
113
113
|
});
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
+
|
|
117
|
+
### 🚀 Tier 2 Pro-Grade Features
|
|
118
|
+
|
|
119
|
+
#### 🌐 Omnichannel Sync
|
|
120
|
+
Connect your bot to Slack and Telegram while maintaining a single user context.
|
|
121
|
+
```typescript
|
|
122
|
+
import { TelegramAdapter, SlackAdapter } from "echo-ai-sdk";
|
|
123
|
+
|
|
124
|
+
const tg = new TelegramAdapter({ bot, token: "TG_TOKEN" });
|
|
125
|
+
await tg.start();
|
|
126
|
+
|
|
127
|
+
const slack = new SlackAdapter({ bot, signingSecret: "...", token: "..." });
|
|
128
|
+
await slack.start();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### 💾 Persistent Session Store
|
|
132
|
+
Move beyond memory. Use `FileSessionStore` for local persistence or implement `SessionStore` for Redis.
|
|
133
|
+
```typescript
|
|
134
|
+
import { FileSessionStore } from "echo-ai-sdk";
|
|
135
|
+
const bot = new CustomerSupportBot({
|
|
136
|
+
sessionStore: new FileSessionStore("./sessions"),
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### 📈 Outcome-Based Billing & ROI
|
|
141
|
+
Track the *real value* of your AI by recording business outcomes.
|
|
142
|
+
```typescript
|
|
143
|
+
// Inside a tool or middleware
|
|
144
|
+
bot.trackOutcome(sessionId, "lead_captured", 50.0);
|
|
145
|
+
|
|
146
|
+
const stats = bot.analytics.getSnapshot();
|
|
147
|
+
console.log(`ROI: ${stats.roi * 100}%`);
|
|
148
|
+
console.log(`Value Generated: $${stats.totalValueGeneratedUsd}`);
|
|
149
|
+
```
|
|
150
|
+
|
|
116
151
|
---
|
|
117
152
|
|
|
118
153
|
## Installation
|
package/dist/index.d.mts
CHANGED
|
@@ -655,6 +655,7 @@ interface ConversationRecord {
|
|
|
655
655
|
topQueries: string[];
|
|
656
656
|
responseTimes: number[];
|
|
657
657
|
model?: string;
|
|
658
|
+
variantId?: string;
|
|
658
659
|
}
|
|
659
660
|
interface AnalyticsSnapshot {
|
|
660
661
|
totalConversations: number;
|
|
@@ -667,6 +668,12 @@ interface AnalyticsSnapshot {
|
|
|
667
668
|
totalCostUsd: number;
|
|
668
669
|
totalValueGeneratedUsd: number;
|
|
669
670
|
roi: number;
|
|
671
|
+
variants?: Record<string, {
|
|
672
|
+
totalConversations: number;
|
|
673
|
+
totalValueGeneratedUsd: number;
|
|
674
|
+
resolutionRate: number;
|
|
675
|
+
roi: number;
|
|
676
|
+
}>;
|
|
670
677
|
avgTokensPerConversation: number;
|
|
671
678
|
avgCostPerConversation: number;
|
|
672
679
|
topQueries: {
|
|
@@ -686,7 +693,7 @@ declare class ConversationAnalytics {
|
|
|
686
693
|
constructor(outcomeTracker?: OutcomeTracker);
|
|
687
694
|
estimateTokens(text: string): number;
|
|
688
695
|
calculateCost(model: string, tokens: number, type?: "input" | "output"): number;
|
|
689
|
-
startConversation(sessionId: string, model?: string): void;
|
|
696
|
+
startConversation(sessionId: string, model?: string, variantId?: string): void;
|
|
690
697
|
recordQuery(sessionId: string, query: string): void;
|
|
691
698
|
recordResponse(sessionId: string, reply: string, latencyMs: number): void;
|
|
692
699
|
markResolved(sessionId: string): void;
|
|
@@ -784,6 +791,43 @@ declare class FileSessionStore implements SessionStore {
|
|
|
784
791
|
clear(): Promise<void>;
|
|
785
792
|
}
|
|
786
793
|
|
|
794
|
+
interface RedactionRule {
|
|
795
|
+
name: string;
|
|
796
|
+
pattern: RegExp;
|
|
797
|
+
placeholder: string;
|
|
798
|
+
}
|
|
799
|
+
declare const DEFAULT_REDACTION_RULES: RedactionRule[];
|
|
800
|
+
declare class PIIRedactor {
|
|
801
|
+
private rules;
|
|
802
|
+
constructor(rules?: RedactionRule[]);
|
|
803
|
+
/** Scans text and replaces any matched patterns with their placeholders. */
|
|
804
|
+
redact(text: string): string;
|
|
805
|
+
/** Adds custom redaction rules to the engine. */
|
|
806
|
+
addRule(rule: RedactionRule): void;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
interface ExperimentVariant {
|
|
810
|
+
id: string;
|
|
811
|
+
weight: number;
|
|
812
|
+
config: Record<string, any>;
|
|
813
|
+
}
|
|
814
|
+
interface Experiment {
|
|
815
|
+
name: string;
|
|
816
|
+
variants: ExperimentVariant[];
|
|
817
|
+
}
|
|
818
|
+
declare class ExperimentManager {
|
|
819
|
+
private experiments;
|
|
820
|
+
constructor(experiments?: Experiment[]);
|
|
821
|
+
registerExperiment(exp: Experiment): void;
|
|
822
|
+
/**
|
|
823
|
+
* Assigns a user to a specific variant based on deterministic hashing of their sessionId.
|
|
824
|
+
* This guarantees 'sticky sessions' where the same user always gets the same A/B test variant.
|
|
825
|
+
*/
|
|
826
|
+
assignVariant(experimentName: string, sessionId: string): string | null;
|
|
827
|
+
/** Retrieves the variant config object for an assigned variant id. */
|
|
828
|
+
getVariantConfig(experimentName: string, variantId: string): Record<string, any> | null;
|
|
829
|
+
}
|
|
830
|
+
|
|
787
831
|
interface SupportBotConfig {
|
|
788
832
|
gateway: AIModelGateway;
|
|
789
833
|
companyName: string;
|
|
@@ -798,6 +842,8 @@ interface SupportBotConfig {
|
|
|
798
842
|
greeting?: string;
|
|
799
843
|
systemPrompt?: string;
|
|
800
844
|
maxIterations?: number;
|
|
845
|
+
enablePIIRedaction?: boolean;
|
|
846
|
+
experiments?: Experiment[];
|
|
801
847
|
}
|
|
802
848
|
type BotMiddleware = (ctx: {
|
|
803
849
|
sessionId: string;
|
|
@@ -817,7 +863,10 @@ declare class CustomerSupportBot {
|
|
|
817
863
|
outcomeTracker: OutcomeTracker;
|
|
818
864
|
sessionStore: SessionStore;
|
|
819
865
|
handoff?: HandoffManager;
|
|
866
|
+
piiRedactor?: PIIRedactor;
|
|
867
|
+
experimentManager?: ExperimentManager;
|
|
820
868
|
greeting: string;
|
|
869
|
+
private defaultSystemPrompt;
|
|
821
870
|
private middlewares;
|
|
822
871
|
constructor(config: SupportBotConfig);
|
|
823
872
|
/** Register a middleware function to run before every chat turn. */
|
|
@@ -933,4 +982,4 @@ declare class TelegramAdapter extends ChannelAdapter {
|
|
|
933
982
|
private sendMessage;
|
|
934
983
|
}
|
|
935
984
|
|
|
936
|
-
export { AIModelGateway, APIConnector, type APIConnectorConfig, AgentExecutor, AgentIterationLimitError, AgentPipeline, AgentRouter, type AgentTelemetry, type AnalyticsSnapshot, type BaseMemoryStore, BaseProvider, BaseSTTProvider, BaseSpeakerRecognizer, BaseTTSProvider, type BotMiddleware, CachedGateway, ChannelAdapter, type ChannelConfig, ChatAgent, type ChatMessage, ChatMessageSchema, type ChatRequest, ChatRequestSchema, type ChatResponse, ChatResponseSchema, ChatWidget, type ChatWidgetConfig, type ChatWidgetTheme, type ChunkOptions, ConfigurationError, ConversationAnalytics, type ConversationRecord, CustomerSupportBot, EchoAI, EchoVoice, type EscalationTrigger, type FetchResult, FileSessionStore, type GatewayMiddleware, GatewayRoutingError, type HandoffConfig, type HandoffEvent, HandoffManager, type IdentificationResult, InMemoryStore, KnowledgeBase, type KnowledgeBaseConfig, MemorySessionStore, MemoryVectorStore, OPENAI_PRICING, OpenAITTS, OpenAIWhisperSTT, type OutcomeRecord, OutcomeTracker, PromptRegistry, PromptTemplate, PromptVersionError, ProviderDependencyError, type STTOptions, type SearchResult, type ServerConfig, type SessionStore, SlackAdapter, type SlackConfig, type SpeakerProfile, StructuredOutputError, type SupportBotConfig, type TTSFormat, type TTSOptions, type TTSResult, type TTSVoice, TelegramAdapter, type TelegramConfig, ToolAgent, type ToolContext, ToolExecutionError, type TranscriptionResult, type TranscriptionSegment, type UsageMetrics, UsageMetricsSchema, ValidationError, type VectorEntry, type VerificationResult, VoiceprintStore, applyRequestMiddleware, applyResponseMiddleware, calculatorTool, chunkText, createChatHandler, createTool, dateTimeTool, startChatServer, webSearchTool };
|
|
985
|
+
export { AIModelGateway, APIConnector, type APIConnectorConfig, AgentExecutor, AgentIterationLimitError, AgentPipeline, AgentRouter, type AgentTelemetry, type AnalyticsSnapshot, type BaseMemoryStore, BaseProvider, BaseSTTProvider, BaseSpeakerRecognizer, BaseTTSProvider, type BotMiddleware, CachedGateway, ChannelAdapter, type ChannelConfig, ChatAgent, type ChatMessage, ChatMessageSchema, type ChatRequest, ChatRequestSchema, type ChatResponse, ChatResponseSchema, ChatWidget, type ChatWidgetConfig, type ChatWidgetTheme, type ChunkOptions, ConfigurationError, ConversationAnalytics, type ConversationRecord, CustomerSupportBot, DEFAULT_REDACTION_RULES, EchoAI, EchoVoice, type EscalationTrigger, type Experiment, ExperimentManager, type ExperimentVariant, type FetchResult, FileSessionStore, type GatewayMiddleware, GatewayRoutingError, type HandoffConfig, type HandoffEvent, HandoffManager, type IdentificationResult, InMemoryStore, KnowledgeBase, type KnowledgeBaseConfig, MemorySessionStore, MemoryVectorStore, OPENAI_PRICING, OpenAITTS, OpenAIWhisperSTT, type OutcomeRecord, OutcomeTracker, PIIRedactor, PromptRegistry, PromptTemplate, PromptVersionError, ProviderDependencyError, type RedactionRule, type STTOptions, type SearchResult, type ServerConfig, type SessionStore, SlackAdapter, type SlackConfig, type SpeakerProfile, StructuredOutputError, type SupportBotConfig, type TTSFormat, type TTSOptions, type TTSResult, type TTSVoice, TelegramAdapter, type TelegramConfig, ToolAgent, type ToolContext, ToolExecutionError, type TranscriptionResult, type TranscriptionSegment, type UsageMetrics, UsageMetricsSchema, ValidationError, type VectorEntry, type VerificationResult, VoiceprintStore, applyRequestMiddleware, applyResponseMiddleware, calculatorTool, chunkText, createChatHandler, createTool, dateTimeTool, startChatServer, webSearchTool };
|
package/dist/index.d.ts
CHANGED
|
@@ -655,6 +655,7 @@ interface ConversationRecord {
|
|
|
655
655
|
topQueries: string[];
|
|
656
656
|
responseTimes: number[];
|
|
657
657
|
model?: string;
|
|
658
|
+
variantId?: string;
|
|
658
659
|
}
|
|
659
660
|
interface AnalyticsSnapshot {
|
|
660
661
|
totalConversations: number;
|
|
@@ -667,6 +668,12 @@ interface AnalyticsSnapshot {
|
|
|
667
668
|
totalCostUsd: number;
|
|
668
669
|
totalValueGeneratedUsd: number;
|
|
669
670
|
roi: number;
|
|
671
|
+
variants?: Record<string, {
|
|
672
|
+
totalConversations: number;
|
|
673
|
+
totalValueGeneratedUsd: number;
|
|
674
|
+
resolutionRate: number;
|
|
675
|
+
roi: number;
|
|
676
|
+
}>;
|
|
670
677
|
avgTokensPerConversation: number;
|
|
671
678
|
avgCostPerConversation: number;
|
|
672
679
|
topQueries: {
|
|
@@ -686,7 +693,7 @@ declare class ConversationAnalytics {
|
|
|
686
693
|
constructor(outcomeTracker?: OutcomeTracker);
|
|
687
694
|
estimateTokens(text: string): number;
|
|
688
695
|
calculateCost(model: string, tokens: number, type?: "input" | "output"): number;
|
|
689
|
-
startConversation(sessionId: string, model?: string): void;
|
|
696
|
+
startConversation(sessionId: string, model?: string, variantId?: string): void;
|
|
690
697
|
recordQuery(sessionId: string, query: string): void;
|
|
691
698
|
recordResponse(sessionId: string, reply: string, latencyMs: number): void;
|
|
692
699
|
markResolved(sessionId: string): void;
|
|
@@ -784,6 +791,43 @@ declare class FileSessionStore implements SessionStore {
|
|
|
784
791
|
clear(): Promise<void>;
|
|
785
792
|
}
|
|
786
793
|
|
|
794
|
+
interface RedactionRule {
|
|
795
|
+
name: string;
|
|
796
|
+
pattern: RegExp;
|
|
797
|
+
placeholder: string;
|
|
798
|
+
}
|
|
799
|
+
declare const DEFAULT_REDACTION_RULES: RedactionRule[];
|
|
800
|
+
declare class PIIRedactor {
|
|
801
|
+
private rules;
|
|
802
|
+
constructor(rules?: RedactionRule[]);
|
|
803
|
+
/** Scans text and replaces any matched patterns with their placeholders. */
|
|
804
|
+
redact(text: string): string;
|
|
805
|
+
/** Adds custom redaction rules to the engine. */
|
|
806
|
+
addRule(rule: RedactionRule): void;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
interface ExperimentVariant {
|
|
810
|
+
id: string;
|
|
811
|
+
weight: number;
|
|
812
|
+
config: Record<string, any>;
|
|
813
|
+
}
|
|
814
|
+
interface Experiment {
|
|
815
|
+
name: string;
|
|
816
|
+
variants: ExperimentVariant[];
|
|
817
|
+
}
|
|
818
|
+
declare class ExperimentManager {
|
|
819
|
+
private experiments;
|
|
820
|
+
constructor(experiments?: Experiment[]);
|
|
821
|
+
registerExperiment(exp: Experiment): void;
|
|
822
|
+
/**
|
|
823
|
+
* Assigns a user to a specific variant based on deterministic hashing of their sessionId.
|
|
824
|
+
* This guarantees 'sticky sessions' where the same user always gets the same A/B test variant.
|
|
825
|
+
*/
|
|
826
|
+
assignVariant(experimentName: string, sessionId: string): string | null;
|
|
827
|
+
/** Retrieves the variant config object for an assigned variant id. */
|
|
828
|
+
getVariantConfig(experimentName: string, variantId: string): Record<string, any> | null;
|
|
829
|
+
}
|
|
830
|
+
|
|
787
831
|
interface SupportBotConfig {
|
|
788
832
|
gateway: AIModelGateway;
|
|
789
833
|
companyName: string;
|
|
@@ -798,6 +842,8 @@ interface SupportBotConfig {
|
|
|
798
842
|
greeting?: string;
|
|
799
843
|
systemPrompt?: string;
|
|
800
844
|
maxIterations?: number;
|
|
845
|
+
enablePIIRedaction?: boolean;
|
|
846
|
+
experiments?: Experiment[];
|
|
801
847
|
}
|
|
802
848
|
type BotMiddleware = (ctx: {
|
|
803
849
|
sessionId: string;
|
|
@@ -817,7 +863,10 @@ declare class CustomerSupportBot {
|
|
|
817
863
|
outcomeTracker: OutcomeTracker;
|
|
818
864
|
sessionStore: SessionStore;
|
|
819
865
|
handoff?: HandoffManager;
|
|
866
|
+
piiRedactor?: PIIRedactor;
|
|
867
|
+
experimentManager?: ExperimentManager;
|
|
820
868
|
greeting: string;
|
|
869
|
+
private defaultSystemPrompt;
|
|
821
870
|
private middlewares;
|
|
822
871
|
constructor(config: SupportBotConfig);
|
|
823
872
|
/** Register a middleware function to run before every chat turn. */
|
|
@@ -933,4 +982,4 @@ declare class TelegramAdapter extends ChannelAdapter {
|
|
|
933
982
|
private sendMessage;
|
|
934
983
|
}
|
|
935
984
|
|
|
936
|
-
export { AIModelGateway, APIConnector, type APIConnectorConfig, AgentExecutor, AgentIterationLimitError, AgentPipeline, AgentRouter, type AgentTelemetry, type AnalyticsSnapshot, type BaseMemoryStore, BaseProvider, BaseSTTProvider, BaseSpeakerRecognizer, BaseTTSProvider, type BotMiddleware, CachedGateway, ChannelAdapter, type ChannelConfig, ChatAgent, type ChatMessage, ChatMessageSchema, type ChatRequest, ChatRequestSchema, type ChatResponse, ChatResponseSchema, ChatWidget, type ChatWidgetConfig, type ChatWidgetTheme, type ChunkOptions, ConfigurationError, ConversationAnalytics, type ConversationRecord, CustomerSupportBot, EchoAI, EchoVoice, type EscalationTrigger, type FetchResult, FileSessionStore, type GatewayMiddleware, GatewayRoutingError, type HandoffConfig, type HandoffEvent, HandoffManager, type IdentificationResult, InMemoryStore, KnowledgeBase, type KnowledgeBaseConfig, MemorySessionStore, MemoryVectorStore, OPENAI_PRICING, OpenAITTS, OpenAIWhisperSTT, type OutcomeRecord, OutcomeTracker, PromptRegistry, PromptTemplate, PromptVersionError, ProviderDependencyError, type STTOptions, type SearchResult, type ServerConfig, type SessionStore, SlackAdapter, type SlackConfig, type SpeakerProfile, StructuredOutputError, type SupportBotConfig, type TTSFormat, type TTSOptions, type TTSResult, type TTSVoice, TelegramAdapter, type TelegramConfig, ToolAgent, type ToolContext, ToolExecutionError, type TranscriptionResult, type TranscriptionSegment, type UsageMetrics, UsageMetricsSchema, ValidationError, type VectorEntry, type VerificationResult, VoiceprintStore, applyRequestMiddleware, applyResponseMiddleware, calculatorTool, chunkText, createChatHandler, createTool, dateTimeTool, startChatServer, webSearchTool };
|
|
985
|
+
export { AIModelGateway, APIConnector, type APIConnectorConfig, AgentExecutor, AgentIterationLimitError, AgentPipeline, AgentRouter, type AgentTelemetry, type AnalyticsSnapshot, type BaseMemoryStore, BaseProvider, BaseSTTProvider, BaseSpeakerRecognizer, BaseTTSProvider, type BotMiddleware, CachedGateway, ChannelAdapter, type ChannelConfig, ChatAgent, type ChatMessage, ChatMessageSchema, type ChatRequest, ChatRequestSchema, type ChatResponse, ChatResponseSchema, ChatWidget, type ChatWidgetConfig, type ChatWidgetTheme, type ChunkOptions, ConfigurationError, ConversationAnalytics, type ConversationRecord, CustomerSupportBot, DEFAULT_REDACTION_RULES, EchoAI, EchoVoice, type EscalationTrigger, type Experiment, ExperimentManager, type ExperimentVariant, type FetchResult, FileSessionStore, type GatewayMiddleware, GatewayRoutingError, type HandoffConfig, type HandoffEvent, HandoffManager, type IdentificationResult, InMemoryStore, KnowledgeBase, type KnowledgeBaseConfig, MemorySessionStore, MemoryVectorStore, OPENAI_PRICING, OpenAITTS, OpenAIWhisperSTT, type OutcomeRecord, OutcomeTracker, PIIRedactor, PromptRegistry, PromptTemplate, PromptVersionError, ProviderDependencyError, type RedactionRule, type STTOptions, type SearchResult, type ServerConfig, type SessionStore, SlackAdapter, type SlackConfig, type SpeakerProfile, StructuredOutputError, type SupportBotConfig, type TTSFormat, type TTSOptions, type TTSResult, type TTSVoice, TelegramAdapter, type TelegramConfig, ToolAgent, type ToolContext, ToolExecutionError, type TranscriptionResult, type TranscriptionSegment, type UsageMetrics, UsageMetricsSchema, ValidationError, type VectorEntry, type VerificationResult, VoiceprintStore, applyRequestMiddleware, applyResponseMiddleware, calculatorTool, chunkText, createChatHandler, createTool, dateTimeTool, startChatServer, webSearchTool };
|
package/dist/index.js
CHANGED
|
@@ -9503,8 +9503,10 @@ __export(index_exports, {
|
|
|
9503
9503
|
ConfigurationError: () => ConfigurationError,
|
|
9504
9504
|
ConversationAnalytics: () => ConversationAnalytics,
|
|
9505
9505
|
CustomerSupportBot: () => CustomerSupportBot,
|
|
9506
|
+
DEFAULT_REDACTION_RULES: () => DEFAULT_REDACTION_RULES,
|
|
9506
9507
|
EchoAI: () => EchoAI,
|
|
9507
9508
|
EchoVoice: () => EchoVoice,
|
|
9509
|
+
ExperimentManager: () => ExperimentManager,
|
|
9508
9510
|
FileSessionStore: () => FileSessionStore,
|
|
9509
9511
|
GatewayRoutingError: () => GatewayRoutingError,
|
|
9510
9512
|
HandoffManager: () => HandoffManager,
|
|
@@ -9516,6 +9518,7 @@ __export(index_exports, {
|
|
|
9516
9518
|
OpenAITTS: () => OpenAITTS,
|
|
9517
9519
|
OpenAIWhisperSTT: () => OpenAIWhisperSTT,
|
|
9518
9520
|
OutcomeTracker: () => OutcomeTracker,
|
|
9521
|
+
PIIRedactor: () => PIIRedactor,
|
|
9519
9522
|
PromptRegistry: () => PromptRegistry,
|
|
9520
9523
|
PromptTemplate: () => PromptTemplate,
|
|
9521
9524
|
PromptVersionError: () => PromptVersionError,
|
|
@@ -15559,7 +15562,7 @@ var ConversationAnalytics = class {
|
|
|
15559
15562
|
const rate = type === "input" ? pricing.input : pricing.output;
|
|
15560
15563
|
return tokens / 1e6 * rate;
|
|
15561
15564
|
}
|
|
15562
|
-
startConversation(sessionId, model = "gpt-4o-mini") {
|
|
15565
|
+
startConversation(sessionId, model = "gpt-4o-mini", variantId) {
|
|
15563
15566
|
if (!this.records.has(sessionId)) {
|
|
15564
15567
|
this.records.set(sessionId, {
|
|
15565
15568
|
sessionId,
|
|
@@ -15571,7 +15574,8 @@ var ConversationAnalytics = class {
|
|
|
15571
15574
|
totalCostUsd: 0,
|
|
15572
15575
|
topQueries: [],
|
|
15573
15576
|
responseTimes: [],
|
|
15574
|
-
model
|
|
15577
|
+
model,
|
|
15578
|
+
variantId
|
|
15575
15579
|
});
|
|
15576
15580
|
}
|
|
15577
15581
|
}
|
|
@@ -15636,6 +15640,31 @@ var ConversationAnalytics = class {
|
|
|
15636
15640
|
const firstConvo = all.length > 0 ? Math.min(...all.map((r2) => r2.startTime)) : now;
|
|
15637
15641
|
const hourSpan = Math.max(1, (now - firstConvo) / 36e5);
|
|
15638
15642
|
const topQueries = [...this.queryFrequency.entries()].sort((a2, b2) => b2[1] - a2[1]).slice(0, 20).map(([query, count]) => ({ query, count }));
|
|
15643
|
+
const variants = {};
|
|
15644
|
+
const groupedByVariant = all.reduce((acc, r2) => {
|
|
15645
|
+
const v2 = r2.variantId || "default";
|
|
15646
|
+
if (!acc[v2]) acc[v2] = [];
|
|
15647
|
+
acc[v2].push(r2);
|
|
15648
|
+
return acc;
|
|
15649
|
+
}, {});
|
|
15650
|
+
for (const [variantId, vRecords] of Object.entries(groupedByVariant)) {
|
|
15651
|
+
const vTotal = vRecords.length;
|
|
15652
|
+
const vResolved = vRecords.filter((r2) => r2.resolved).length;
|
|
15653
|
+
let vValue = 0;
|
|
15654
|
+
if (this.outcomeTracker) {
|
|
15655
|
+
vRecords.forEach((r2) => {
|
|
15656
|
+
const outRecords = this.outcomeTracker.outcomes?.get(r2.sessionId) || [];
|
|
15657
|
+
vValue += outRecords.reduce((s2, o2) => s2 + o2.valueUsd, 0);
|
|
15658
|
+
});
|
|
15659
|
+
}
|
|
15660
|
+
const vCost = vRecords.reduce((s2, r2) => s2 + r2.totalCostUsd, 0);
|
|
15661
|
+
variants[variantId] = {
|
|
15662
|
+
totalConversations: vTotal,
|
|
15663
|
+
totalValueGeneratedUsd: vValue,
|
|
15664
|
+
resolutionRate: vTotal > 0 ? vResolved / vTotal : 0,
|
|
15665
|
+
roi: vCost > 0 ? (vValue - vCost) / vCost : 0
|
|
15666
|
+
};
|
|
15667
|
+
}
|
|
15639
15668
|
return {
|
|
15640
15669
|
totalConversations: total,
|
|
15641
15670
|
resolvedCount: resolved,
|
|
@@ -15905,6 +15934,93 @@ var OutcomeTracker = class {
|
|
|
15905
15934
|
}
|
|
15906
15935
|
};
|
|
15907
15936
|
|
|
15937
|
+
// src/analytics/redact.ts
|
|
15938
|
+
var DEFAULT_REDACTION_RULES = [
|
|
15939
|
+
{
|
|
15940
|
+
name: "email",
|
|
15941
|
+
pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
15942
|
+
placeholder: "[REDACTED_EMAIL]"
|
|
15943
|
+
},
|
|
15944
|
+
{
|
|
15945
|
+
name: "phone",
|
|
15946
|
+
pattern: /\b(?:\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
15947
|
+
placeholder: "[REDACTED_PHONE]"
|
|
15948
|
+
},
|
|
15949
|
+
{
|
|
15950
|
+
name: "credit_card",
|
|
15951
|
+
// Standard 16 digit match with optional dashes or spaces
|
|
15952
|
+
pattern: /\b(?:\d[ -]*?){13,16}\b/g,
|
|
15953
|
+
placeholder: "[REDACTED_CC]"
|
|
15954
|
+
},
|
|
15955
|
+
{
|
|
15956
|
+
name: "ssn",
|
|
15957
|
+
// Standard US SSN
|
|
15958
|
+
pattern: /\b\d{3}[-.\s]?\d{2}[-.\s]?\d{4}\b/g,
|
|
15959
|
+
placeholder: "[REDACTED_SSN]"
|
|
15960
|
+
}
|
|
15961
|
+
];
|
|
15962
|
+
var PIIRedactor = class {
|
|
15963
|
+
rules;
|
|
15964
|
+
constructor(rules) {
|
|
15965
|
+
this.rules = rules || DEFAULT_REDACTION_RULES;
|
|
15966
|
+
}
|
|
15967
|
+
/** Scans text and replaces any matched patterns with their placeholders. */
|
|
15968
|
+
redact(text) {
|
|
15969
|
+
if (!text) return text;
|
|
15970
|
+
let redacted = text;
|
|
15971
|
+
for (const rule of this.rules) {
|
|
15972
|
+
redacted = redacted.replace(rule.pattern, rule.placeholder);
|
|
15973
|
+
}
|
|
15974
|
+
return redacted;
|
|
15975
|
+
}
|
|
15976
|
+
/** Adds custom redaction rules to the engine. */
|
|
15977
|
+
addRule(rule) {
|
|
15978
|
+
this.rules.push(rule);
|
|
15979
|
+
}
|
|
15980
|
+
};
|
|
15981
|
+
|
|
15982
|
+
// src/analytics/experiment.ts
|
|
15983
|
+
var crypto2 = __toESM(require("crypto"));
|
|
15984
|
+
var ExperimentManager = class {
|
|
15985
|
+
experiments = /* @__PURE__ */ new Map();
|
|
15986
|
+
constructor(experiments) {
|
|
15987
|
+
if (experiments) {
|
|
15988
|
+
experiments.forEach((e2) => this.registerExperiment(e2));
|
|
15989
|
+
}
|
|
15990
|
+
}
|
|
15991
|
+
registerExperiment(exp) {
|
|
15992
|
+
this.experiments.set(exp.name, exp);
|
|
15993
|
+
}
|
|
15994
|
+
/**
|
|
15995
|
+
* Assigns a user to a specific variant based on deterministic hashing of their sessionId.
|
|
15996
|
+
* This guarantees 'sticky sessions' where the same user always gets the same A/B test variant.
|
|
15997
|
+
*/
|
|
15998
|
+
assignVariant(experimentName, sessionId) {
|
|
15999
|
+
const exp = this.experiments.get(experimentName);
|
|
16000
|
+
if (!exp || exp.variants.length === 0) return null;
|
|
16001
|
+
if (exp.variants.length === 1) return exp.variants[0].id;
|
|
16002
|
+
const totalWeight = exp.variants.reduce((sum, v2) => sum + v2.weight, 0);
|
|
16003
|
+
const hash = crypto2.createHash("md5").update(`${experimentName}_${sessionId}`).digest("hex");
|
|
16004
|
+
const hashInt = parseInt(hash.slice(0, 8), 16);
|
|
16005
|
+
const bucket = hashInt % totalWeight;
|
|
16006
|
+
let cumulative = 0;
|
|
16007
|
+
for (const variant of exp.variants) {
|
|
16008
|
+
cumulative += variant.weight;
|
|
16009
|
+
if (bucket < cumulative) {
|
|
16010
|
+
return variant.id;
|
|
16011
|
+
}
|
|
16012
|
+
}
|
|
16013
|
+
return exp.variants[0].id;
|
|
16014
|
+
}
|
|
16015
|
+
/** Retrieves the variant config object for an assigned variant id. */
|
|
16016
|
+
getVariantConfig(experimentName, variantId) {
|
|
16017
|
+
const exp = this.experiments.get(experimentName);
|
|
16018
|
+
if (!exp) return null;
|
|
16019
|
+
const v2 = exp.variants.find((v3) => v3.id === variantId);
|
|
16020
|
+
return v2 ? v2.config : null;
|
|
16021
|
+
}
|
|
16022
|
+
};
|
|
16023
|
+
|
|
15908
16024
|
// src/widget/bot.ts
|
|
15909
16025
|
var CustomerSupportBot = class {
|
|
15910
16026
|
executor;
|
|
@@ -15914,7 +16030,10 @@ var CustomerSupportBot = class {
|
|
|
15914
16030
|
outcomeTracker;
|
|
15915
16031
|
sessionStore;
|
|
15916
16032
|
handoff;
|
|
16033
|
+
piiRedactor;
|
|
16034
|
+
experimentManager;
|
|
15917
16035
|
greeting;
|
|
16036
|
+
defaultSystemPrompt;
|
|
15918
16037
|
middlewares = [];
|
|
15919
16038
|
constructor(config) {
|
|
15920
16039
|
this.outcomeTracker = new OutcomeTracker();
|
|
@@ -15926,6 +16045,12 @@ var CustomerSupportBot = class {
|
|
|
15926
16045
|
if (config.handoff) {
|
|
15927
16046
|
this.handoff = new HandoffManager(config.handoff);
|
|
15928
16047
|
}
|
|
16048
|
+
if (config.enablePIIRedaction) {
|
|
16049
|
+
this.piiRedactor = new PIIRedactor();
|
|
16050
|
+
}
|
|
16051
|
+
if (config.experiments && config.experiments.length > 0) {
|
|
16052
|
+
this.experimentManager = new ExperimentManager(config.experiments);
|
|
16053
|
+
}
|
|
15929
16054
|
const tools = [];
|
|
15930
16055
|
if (config.apiConnector) {
|
|
15931
16056
|
this.connector = new APIConnector(config.apiConnector);
|
|
@@ -15957,11 +16082,12 @@ Your role:
|
|
|
15957
16082
|
- If you can't find an answer, politely suggest contacting human support
|
|
15958
16083
|
- Always maintain a warm, professional tone
|
|
15959
16084
|
- Never make up data \u2014 always verify through the API or context when available`;
|
|
16085
|
+
this.defaultSystemPrompt = config.systemPrompt || defaultSystemPrompt;
|
|
15960
16086
|
this.executor = new AgentExecutor({
|
|
15961
16087
|
gateway: config.gateway,
|
|
15962
16088
|
memory: config.memory || new InMemoryStore(100),
|
|
15963
16089
|
tools,
|
|
15964
|
-
systemPrompt:
|
|
16090
|
+
systemPrompt: this.defaultSystemPrompt,
|
|
15965
16091
|
telemetry: config.telemetry
|
|
15966
16092
|
});
|
|
15967
16093
|
this.greeting = config.greeting || `Hello! \u{1F44B} Welcome to ${config.companyName}. How can I help you today?`;
|
|
@@ -15976,15 +16102,28 @@ Your role:
|
|
|
15976
16102
|
}
|
|
15977
16103
|
/** Process a customer message and return the bot's response. */
|
|
15978
16104
|
async chat(sessionId, message) {
|
|
15979
|
-
this.
|
|
15980
|
-
|
|
16105
|
+
const activeMessage = this.piiRedactor ? this.piiRedactor.redact(message) : message;
|
|
16106
|
+
let variantId;
|
|
16107
|
+
if (this.experimentManager) {
|
|
16108
|
+
variantId = this.experimentManager.assignVariant("default_experiment", sessionId) || void 0;
|
|
16109
|
+
if (variantId) {
|
|
16110
|
+
const config = this.experimentManager.getVariantConfig("default_experiment", variantId);
|
|
16111
|
+
if (config && config.systemPrompt) {
|
|
16112
|
+
this.executor.systemPrompt = config.systemPrompt;
|
|
16113
|
+
} else {
|
|
16114
|
+
this.executor.systemPrompt = this.defaultSystemPrompt;
|
|
16115
|
+
}
|
|
16116
|
+
}
|
|
16117
|
+
}
|
|
16118
|
+
this.analytics.startConversation(sessionId, "gpt-4o-mini", variantId);
|
|
16119
|
+
this.analytics.recordQuery(sessionId, activeMessage);
|
|
15981
16120
|
const startTime = Date.now();
|
|
15982
16121
|
for (const mw of this.middlewares) {
|
|
15983
|
-
const result2 = await mw({ sessionId, message, bot: this });
|
|
16122
|
+
const result2 = await mw({ sessionId, message: activeMessage, bot: this });
|
|
15984
16123
|
if (typeof result2 === "string") return result2;
|
|
15985
16124
|
}
|
|
15986
16125
|
if (this.handoff) {
|
|
15987
|
-
const trigger = this.handoff.shouldEscalate(sessionId,
|
|
16126
|
+
const trigger = this.handoff.shouldEscalate(sessionId, activeMessage);
|
|
15988
16127
|
if (trigger) {
|
|
15989
16128
|
const history = await this.executor.memory.getMessages(sessionId);
|
|
15990
16129
|
let summary = "Customer needs assistance.";
|
|
@@ -15992,23 +16131,23 @@ Your role:
|
|
|
15992
16131
|
summary = await this.executor.gateway.chat([
|
|
15993
16132
|
{ role: "system", content: "Summarize the customer's problem in one concise sentence for a human support agent context." },
|
|
15994
16133
|
...history,
|
|
15995
|
-
{ role: "user", content:
|
|
16134
|
+
{ role: "user", content: activeMessage }
|
|
15996
16135
|
]);
|
|
15997
16136
|
} catch {
|
|
15998
16137
|
}
|
|
15999
|
-
const reply = await this.handoff.escalate(sessionId, trigger, history,
|
|
16138
|
+
const reply = await this.handoff.escalate(sessionId, trigger, history, activeMessage, summary);
|
|
16000
16139
|
this.analytics.markHandedOff(sessionId);
|
|
16001
16140
|
return reply;
|
|
16002
16141
|
}
|
|
16003
16142
|
}
|
|
16004
|
-
let enhancedMessage =
|
|
16143
|
+
let enhancedMessage = activeMessage;
|
|
16005
16144
|
if (this.knowledgeBase) {
|
|
16006
|
-
const context = await this.knowledgeBase.query(
|
|
16145
|
+
const context = await this.knowledgeBase.query(activeMessage);
|
|
16007
16146
|
if (context) {
|
|
16008
16147
|
enhancedMessage = `Context from Knowledge Base:
|
|
16009
16148
|
${context}
|
|
16010
16149
|
|
|
16011
|
-
User Question: ${
|
|
16150
|
+
User Question: ${activeMessage}`;
|
|
16012
16151
|
}
|
|
16013
16152
|
}
|
|
16014
16153
|
try {
|
|
@@ -16019,7 +16158,7 @@ User Question: ${message}`;
|
|
|
16019
16158
|
return reply;
|
|
16020
16159
|
} catch (e2) {
|
|
16021
16160
|
if (this.handoff) {
|
|
16022
|
-
return this.handoff.escalate(sessionId, "low_confidence", [],
|
|
16161
|
+
return this.handoff.escalate(sessionId, "low_confidence", [], activeMessage, `Error: ${e2.message}`);
|
|
16023
16162
|
}
|
|
16024
16163
|
throw e2;
|
|
16025
16164
|
}
|
|
@@ -16326,8 +16465,10 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
16326
16465
|
ConfigurationError,
|
|
16327
16466
|
ConversationAnalytics,
|
|
16328
16467
|
CustomerSupportBot,
|
|
16468
|
+
DEFAULT_REDACTION_RULES,
|
|
16329
16469
|
EchoAI,
|
|
16330
16470
|
EchoVoice,
|
|
16471
|
+
ExperimentManager,
|
|
16331
16472
|
FileSessionStore,
|
|
16332
16473
|
GatewayRoutingError,
|
|
16333
16474
|
HandoffManager,
|
|
@@ -16339,6 +16480,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
16339
16480
|
OpenAITTS,
|
|
16340
16481
|
OpenAIWhisperSTT,
|
|
16341
16482
|
OutcomeTracker,
|
|
16483
|
+
PIIRedactor,
|
|
16342
16484
|
PromptRegistry,
|
|
16343
16485
|
PromptTemplate,
|
|
16344
16486
|
PromptVersionError,
|
package/dist/index.mjs
CHANGED
|
@@ -13270,7 +13270,7 @@ var ConversationAnalytics = class {
|
|
|
13270
13270
|
const rate = type === "input" ? pricing.input : pricing.output;
|
|
13271
13271
|
return tokens / 1e6 * rate;
|
|
13272
13272
|
}
|
|
13273
|
-
startConversation(sessionId, model = "gpt-4o-mini") {
|
|
13273
|
+
startConversation(sessionId, model = "gpt-4o-mini", variantId) {
|
|
13274
13274
|
if (!this.records.has(sessionId)) {
|
|
13275
13275
|
this.records.set(sessionId, {
|
|
13276
13276
|
sessionId,
|
|
@@ -13282,7 +13282,8 @@ var ConversationAnalytics = class {
|
|
|
13282
13282
|
totalCostUsd: 0,
|
|
13283
13283
|
topQueries: [],
|
|
13284
13284
|
responseTimes: [],
|
|
13285
|
-
model
|
|
13285
|
+
model,
|
|
13286
|
+
variantId
|
|
13286
13287
|
});
|
|
13287
13288
|
}
|
|
13288
13289
|
}
|
|
@@ -13347,6 +13348,31 @@ var ConversationAnalytics = class {
|
|
|
13347
13348
|
const firstConvo = all.length > 0 ? Math.min(...all.map((r) => r.startTime)) : now;
|
|
13348
13349
|
const hourSpan = Math.max(1, (now - firstConvo) / 36e5);
|
|
13349
13350
|
const topQueries = [...this.queryFrequency.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20).map(([query, count]) => ({ query, count }));
|
|
13351
|
+
const variants = {};
|
|
13352
|
+
const groupedByVariant = all.reduce((acc, r) => {
|
|
13353
|
+
const v = r.variantId || "default";
|
|
13354
|
+
if (!acc[v]) acc[v] = [];
|
|
13355
|
+
acc[v].push(r);
|
|
13356
|
+
return acc;
|
|
13357
|
+
}, {});
|
|
13358
|
+
for (const [variantId, vRecords] of Object.entries(groupedByVariant)) {
|
|
13359
|
+
const vTotal = vRecords.length;
|
|
13360
|
+
const vResolved = vRecords.filter((r) => r.resolved).length;
|
|
13361
|
+
let vValue = 0;
|
|
13362
|
+
if (this.outcomeTracker) {
|
|
13363
|
+
vRecords.forEach((r) => {
|
|
13364
|
+
const outRecords = this.outcomeTracker.outcomes?.get(r.sessionId) || [];
|
|
13365
|
+
vValue += outRecords.reduce((s, o) => s + o.valueUsd, 0);
|
|
13366
|
+
});
|
|
13367
|
+
}
|
|
13368
|
+
const vCost = vRecords.reduce((s, r) => s + r.totalCostUsd, 0);
|
|
13369
|
+
variants[variantId] = {
|
|
13370
|
+
totalConversations: vTotal,
|
|
13371
|
+
totalValueGeneratedUsd: vValue,
|
|
13372
|
+
resolutionRate: vTotal > 0 ? vResolved / vTotal : 0,
|
|
13373
|
+
roi: vCost > 0 ? (vValue - vCost) / vCost : 0
|
|
13374
|
+
};
|
|
13375
|
+
}
|
|
13350
13376
|
return {
|
|
13351
13377
|
totalConversations: total,
|
|
13352
13378
|
resolvedCount: resolved,
|
|
@@ -13616,6 +13642,93 @@ var OutcomeTracker = class {
|
|
|
13616
13642
|
}
|
|
13617
13643
|
};
|
|
13618
13644
|
|
|
13645
|
+
// src/analytics/redact.ts
|
|
13646
|
+
var DEFAULT_REDACTION_RULES = [
|
|
13647
|
+
{
|
|
13648
|
+
name: "email",
|
|
13649
|
+
pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
13650
|
+
placeholder: "[REDACTED_EMAIL]"
|
|
13651
|
+
},
|
|
13652
|
+
{
|
|
13653
|
+
name: "phone",
|
|
13654
|
+
pattern: /\b(?:\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
13655
|
+
placeholder: "[REDACTED_PHONE]"
|
|
13656
|
+
},
|
|
13657
|
+
{
|
|
13658
|
+
name: "credit_card",
|
|
13659
|
+
// Standard 16 digit match with optional dashes or spaces
|
|
13660
|
+
pattern: /\b(?:\d[ -]*?){13,16}\b/g,
|
|
13661
|
+
placeholder: "[REDACTED_CC]"
|
|
13662
|
+
},
|
|
13663
|
+
{
|
|
13664
|
+
name: "ssn",
|
|
13665
|
+
// Standard US SSN
|
|
13666
|
+
pattern: /\b\d{3}[-.\s]?\d{2}[-.\s]?\d{4}\b/g,
|
|
13667
|
+
placeholder: "[REDACTED_SSN]"
|
|
13668
|
+
}
|
|
13669
|
+
];
|
|
13670
|
+
var PIIRedactor = class {
|
|
13671
|
+
rules;
|
|
13672
|
+
constructor(rules) {
|
|
13673
|
+
this.rules = rules || DEFAULT_REDACTION_RULES;
|
|
13674
|
+
}
|
|
13675
|
+
/** Scans text and replaces any matched patterns with their placeholders. */
|
|
13676
|
+
redact(text) {
|
|
13677
|
+
if (!text) return text;
|
|
13678
|
+
let redacted = text;
|
|
13679
|
+
for (const rule of this.rules) {
|
|
13680
|
+
redacted = redacted.replace(rule.pattern, rule.placeholder);
|
|
13681
|
+
}
|
|
13682
|
+
return redacted;
|
|
13683
|
+
}
|
|
13684
|
+
/** Adds custom redaction rules to the engine. */
|
|
13685
|
+
addRule(rule) {
|
|
13686
|
+
this.rules.push(rule);
|
|
13687
|
+
}
|
|
13688
|
+
};
|
|
13689
|
+
|
|
13690
|
+
// src/analytics/experiment.ts
|
|
13691
|
+
import * as crypto2 from "crypto";
|
|
13692
|
+
var ExperimentManager = class {
|
|
13693
|
+
experiments = /* @__PURE__ */ new Map();
|
|
13694
|
+
constructor(experiments) {
|
|
13695
|
+
if (experiments) {
|
|
13696
|
+
experiments.forEach((e) => this.registerExperiment(e));
|
|
13697
|
+
}
|
|
13698
|
+
}
|
|
13699
|
+
registerExperiment(exp) {
|
|
13700
|
+
this.experiments.set(exp.name, exp);
|
|
13701
|
+
}
|
|
13702
|
+
/**
|
|
13703
|
+
* Assigns a user to a specific variant based on deterministic hashing of their sessionId.
|
|
13704
|
+
* This guarantees 'sticky sessions' where the same user always gets the same A/B test variant.
|
|
13705
|
+
*/
|
|
13706
|
+
assignVariant(experimentName, sessionId) {
|
|
13707
|
+
const exp = this.experiments.get(experimentName);
|
|
13708
|
+
if (!exp || exp.variants.length === 0) return null;
|
|
13709
|
+
if (exp.variants.length === 1) return exp.variants[0].id;
|
|
13710
|
+
const totalWeight = exp.variants.reduce((sum, v) => sum + v.weight, 0);
|
|
13711
|
+
const hash = crypto2.createHash("md5").update(`${experimentName}_${sessionId}`).digest("hex");
|
|
13712
|
+
const hashInt = parseInt(hash.slice(0, 8), 16);
|
|
13713
|
+
const bucket = hashInt % totalWeight;
|
|
13714
|
+
let cumulative = 0;
|
|
13715
|
+
for (const variant of exp.variants) {
|
|
13716
|
+
cumulative += variant.weight;
|
|
13717
|
+
if (bucket < cumulative) {
|
|
13718
|
+
return variant.id;
|
|
13719
|
+
}
|
|
13720
|
+
}
|
|
13721
|
+
return exp.variants[0].id;
|
|
13722
|
+
}
|
|
13723
|
+
/** Retrieves the variant config object for an assigned variant id. */
|
|
13724
|
+
getVariantConfig(experimentName, variantId) {
|
|
13725
|
+
const exp = this.experiments.get(experimentName);
|
|
13726
|
+
if (!exp) return null;
|
|
13727
|
+
const v = exp.variants.find((v2) => v2.id === variantId);
|
|
13728
|
+
return v ? v.config : null;
|
|
13729
|
+
}
|
|
13730
|
+
};
|
|
13731
|
+
|
|
13619
13732
|
// src/widget/bot.ts
|
|
13620
13733
|
var CustomerSupportBot = class {
|
|
13621
13734
|
executor;
|
|
@@ -13625,7 +13738,10 @@ var CustomerSupportBot = class {
|
|
|
13625
13738
|
outcomeTracker;
|
|
13626
13739
|
sessionStore;
|
|
13627
13740
|
handoff;
|
|
13741
|
+
piiRedactor;
|
|
13742
|
+
experimentManager;
|
|
13628
13743
|
greeting;
|
|
13744
|
+
defaultSystemPrompt;
|
|
13629
13745
|
middlewares = [];
|
|
13630
13746
|
constructor(config) {
|
|
13631
13747
|
this.outcomeTracker = new OutcomeTracker();
|
|
@@ -13637,6 +13753,12 @@ var CustomerSupportBot = class {
|
|
|
13637
13753
|
if (config.handoff) {
|
|
13638
13754
|
this.handoff = new HandoffManager(config.handoff);
|
|
13639
13755
|
}
|
|
13756
|
+
if (config.enablePIIRedaction) {
|
|
13757
|
+
this.piiRedactor = new PIIRedactor();
|
|
13758
|
+
}
|
|
13759
|
+
if (config.experiments && config.experiments.length > 0) {
|
|
13760
|
+
this.experimentManager = new ExperimentManager(config.experiments);
|
|
13761
|
+
}
|
|
13640
13762
|
const tools = [];
|
|
13641
13763
|
if (config.apiConnector) {
|
|
13642
13764
|
this.connector = new APIConnector(config.apiConnector);
|
|
@@ -13668,11 +13790,12 @@ Your role:
|
|
|
13668
13790
|
- If you can't find an answer, politely suggest contacting human support
|
|
13669
13791
|
- Always maintain a warm, professional tone
|
|
13670
13792
|
- Never make up data \u2014 always verify through the API or context when available`;
|
|
13793
|
+
this.defaultSystemPrompt = config.systemPrompt || defaultSystemPrompt;
|
|
13671
13794
|
this.executor = new AgentExecutor({
|
|
13672
13795
|
gateway: config.gateway,
|
|
13673
13796
|
memory: config.memory || new InMemoryStore(100),
|
|
13674
13797
|
tools,
|
|
13675
|
-
systemPrompt:
|
|
13798
|
+
systemPrompt: this.defaultSystemPrompt,
|
|
13676
13799
|
telemetry: config.telemetry
|
|
13677
13800
|
});
|
|
13678
13801
|
this.greeting = config.greeting || `Hello! \u{1F44B} Welcome to ${config.companyName}. How can I help you today?`;
|
|
@@ -13687,15 +13810,28 @@ Your role:
|
|
|
13687
13810
|
}
|
|
13688
13811
|
/** Process a customer message and return the bot's response. */
|
|
13689
13812
|
async chat(sessionId, message) {
|
|
13690
|
-
this.
|
|
13691
|
-
|
|
13813
|
+
const activeMessage = this.piiRedactor ? this.piiRedactor.redact(message) : message;
|
|
13814
|
+
let variantId;
|
|
13815
|
+
if (this.experimentManager) {
|
|
13816
|
+
variantId = this.experimentManager.assignVariant("default_experiment", sessionId) || void 0;
|
|
13817
|
+
if (variantId) {
|
|
13818
|
+
const config = this.experimentManager.getVariantConfig("default_experiment", variantId);
|
|
13819
|
+
if (config && config.systemPrompt) {
|
|
13820
|
+
this.executor.systemPrompt = config.systemPrompt;
|
|
13821
|
+
} else {
|
|
13822
|
+
this.executor.systemPrompt = this.defaultSystemPrompt;
|
|
13823
|
+
}
|
|
13824
|
+
}
|
|
13825
|
+
}
|
|
13826
|
+
this.analytics.startConversation(sessionId, "gpt-4o-mini", variantId);
|
|
13827
|
+
this.analytics.recordQuery(sessionId, activeMessage);
|
|
13692
13828
|
const startTime = Date.now();
|
|
13693
13829
|
for (const mw of this.middlewares) {
|
|
13694
|
-
const result2 = await mw({ sessionId, message, bot: this });
|
|
13830
|
+
const result2 = await mw({ sessionId, message: activeMessage, bot: this });
|
|
13695
13831
|
if (typeof result2 === "string") return result2;
|
|
13696
13832
|
}
|
|
13697
13833
|
if (this.handoff) {
|
|
13698
|
-
const trigger = this.handoff.shouldEscalate(sessionId,
|
|
13834
|
+
const trigger = this.handoff.shouldEscalate(sessionId, activeMessage);
|
|
13699
13835
|
if (trigger) {
|
|
13700
13836
|
const history = await this.executor.memory.getMessages(sessionId);
|
|
13701
13837
|
let summary = "Customer needs assistance.";
|
|
@@ -13703,23 +13839,23 @@ Your role:
|
|
|
13703
13839
|
summary = await this.executor.gateway.chat([
|
|
13704
13840
|
{ role: "system", content: "Summarize the customer's problem in one concise sentence for a human support agent context." },
|
|
13705
13841
|
...history,
|
|
13706
|
-
{ role: "user", content:
|
|
13842
|
+
{ role: "user", content: activeMessage }
|
|
13707
13843
|
]);
|
|
13708
13844
|
} catch {
|
|
13709
13845
|
}
|
|
13710
|
-
const reply = await this.handoff.escalate(sessionId, trigger, history,
|
|
13846
|
+
const reply = await this.handoff.escalate(sessionId, trigger, history, activeMessage, summary);
|
|
13711
13847
|
this.analytics.markHandedOff(sessionId);
|
|
13712
13848
|
return reply;
|
|
13713
13849
|
}
|
|
13714
13850
|
}
|
|
13715
|
-
let enhancedMessage =
|
|
13851
|
+
let enhancedMessage = activeMessage;
|
|
13716
13852
|
if (this.knowledgeBase) {
|
|
13717
|
-
const context = await this.knowledgeBase.query(
|
|
13853
|
+
const context = await this.knowledgeBase.query(activeMessage);
|
|
13718
13854
|
if (context) {
|
|
13719
13855
|
enhancedMessage = `Context from Knowledge Base:
|
|
13720
13856
|
${context}
|
|
13721
13857
|
|
|
13722
|
-
User Question: ${
|
|
13858
|
+
User Question: ${activeMessage}`;
|
|
13723
13859
|
}
|
|
13724
13860
|
}
|
|
13725
13861
|
try {
|
|
@@ -13730,7 +13866,7 @@ User Question: ${message}`;
|
|
|
13730
13866
|
return reply;
|
|
13731
13867
|
} catch (e) {
|
|
13732
13868
|
if (this.handoff) {
|
|
13733
|
-
return this.handoff.escalate(sessionId, "low_confidence", [],
|
|
13869
|
+
return this.handoff.escalate(sessionId, "low_confidence", [], activeMessage, `Error: ${e.message}`);
|
|
13734
13870
|
}
|
|
13735
13871
|
throw e;
|
|
13736
13872
|
}
|
|
@@ -14036,8 +14172,10 @@ export {
|
|
|
14036
14172
|
ConfigurationError,
|
|
14037
14173
|
ConversationAnalytics,
|
|
14038
14174
|
CustomerSupportBot,
|
|
14175
|
+
DEFAULT_REDACTION_RULES,
|
|
14039
14176
|
EchoAI,
|
|
14040
14177
|
EchoVoice,
|
|
14178
|
+
ExperimentManager,
|
|
14041
14179
|
FileSessionStore,
|
|
14042
14180
|
GatewayRoutingError,
|
|
14043
14181
|
HandoffManager,
|
|
@@ -14049,6 +14187,7 @@ export {
|
|
|
14049
14187
|
OpenAITTS,
|
|
14050
14188
|
OpenAIWhisperSTT,
|
|
14051
14189
|
OutcomeTracker,
|
|
14190
|
+
PIIRedactor,
|
|
14052
14191
|
PromptRegistry,
|
|
14053
14192
|
PromptTemplate,
|
|
14054
14193
|
PromptVersionError,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "echo-ai-sdk-ts",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Echo AI SDK: Tier
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "Echo AI SDK: Tier 3 Enterprise Premium (PII Redaction, A/B Testing, Omnichannel, RAG)",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|