@vfarcic/dot-ai 0.194.0 → 0.195.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.
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Circuit Breaker Implementation
3
+ *
4
+ * Provides resilience for external service calls by preventing cascading failures.
5
+ * Implements the circuit breaker pattern with three states:
6
+ * - CLOSED: Normal operation, requests flow through
7
+ * - OPEN: Failure threshold reached, requests are blocked
8
+ * - HALF_OPEN: Testing recovery, limited requests allowed
9
+ */
10
+ import { Logger } from './error-handling';
11
+ /**
12
+ * Circuit breaker states
13
+ */
14
+ export declare enum CircuitState {
15
+ CLOSED = "closed",
16
+ OPEN = "open",
17
+ HALF_OPEN = "half_open"
18
+ }
19
+ /**
20
+ * Configuration options for circuit breaker
21
+ */
22
+ export interface CircuitBreakerConfig {
23
+ /** Number of consecutive failures before opening circuit (default: 3) */
24
+ failureThreshold?: number;
25
+ /** Time in milliseconds to wait before transitioning from OPEN to HALF_OPEN (default: 30000) */
26
+ cooldownPeriodMs?: number;
27
+ /** Number of requests to allow in HALF_OPEN state before deciding (default: 1) */
28
+ halfOpenMaxAttempts?: number;
29
+ /** Callback when state changes */
30
+ onStateChange?: (from: CircuitState, to: CircuitState, name: string) => void;
31
+ }
32
+ /**
33
+ * Statistics about circuit breaker state
34
+ */
35
+ export interface CircuitBreakerStats {
36
+ state: CircuitState;
37
+ consecutiveFailures: number;
38
+ totalFailures: number;
39
+ totalSuccesses: number;
40
+ lastFailureTime?: Date;
41
+ lastSuccessTime?: Date;
42
+ openedAt?: Date;
43
+ halfOpenAttempts: number;
44
+ }
45
+ /**
46
+ * Error thrown when circuit is open and blocking requests
47
+ */
48
+ export declare class CircuitOpenError extends Error {
49
+ readonly circuitName: string;
50
+ readonly remainingCooldownMs: number;
51
+ readonly state: CircuitState;
52
+ constructor(circuitName: string, remainingCooldownMs: number);
53
+ }
54
+ /**
55
+ * Circuit Breaker implementation
56
+ *
57
+ * Usage:
58
+ * ```typescript
59
+ * const breaker = new CircuitBreaker('embedding-api', { failureThreshold: 3 });
60
+ *
61
+ * try {
62
+ * const result = await breaker.execute(() => fetchFromAPI());
63
+ * } catch (error) {
64
+ * if (error instanceof CircuitOpenError) {
65
+ * // Circuit is open, handle gracefully
66
+ * }
67
+ * }
68
+ * ```
69
+ */
70
+ export declare class CircuitBreaker {
71
+ private readonly name;
72
+ private readonly config;
73
+ private readonly logger;
74
+ private state;
75
+ private consecutiveFailures;
76
+ private totalFailures;
77
+ private totalSuccesses;
78
+ private lastFailureTime?;
79
+ private lastSuccessTime?;
80
+ private openedAt?;
81
+ private halfOpenAttempts;
82
+ constructor(name: string, config?: CircuitBreakerConfig, logger?: Logger);
83
+ /**
84
+ * Execute an operation through the circuit breaker
85
+ * @throws CircuitOpenError if circuit is open
86
+ * @throws Original error if operation fails
87
+ */
88
+ execute<T>(operation: () => Promise<T>): Promise<T>;
89
+ /**
90
+ * Record a successful operation
91
+ */
92
+ recordSuccess(): void;
93
+ /**
94
+ * Record a failed operation
95
+ */
96
+ recordFailure(error?: Error): void;
97
+ /**
98
+ * Reset the circuit breaker to initial state
99
+ */
100
+ reset(): void;
101
+ /**
102
+ * Get the current circuit state
103
+ */
104
+ getState(): CircuitState;
105
+ /**
106
+ * Get circuit breaker statistics
107
+ */
108
+ getStats(): CircuitBreakerStats;
109
+ /**
110
+ * Check if circuit is currently open (blocking requests)
111
+ */
112
+ isOpen(): boolean;
113
+ /**
114
+ * Get the circuit breaker name
115
+ */
116
+ getName(): string;
117
+ /**
118
+ * Check if a request can be executed
119
+ */
120
+ private canExecute;
121
+ /**
122
+ * Check if cooldown period has elapsed
123
+ */
124
+ private shouldTransitionToHalfOpen;
125
+ /**
126
+ * Get remaining cooldown time in milliseconds
127
+ */
128
+ private getRemainingCooldown;
129
+ /**
130
+ * Transition to a new state
131
+ */
132
+ private transitionTo;
133
+ }
134
+ /**
135
+ * Factory for creating circuit breakers with shared configuration
136
+ */
137
+ export declare class CircuitBreakerFactory {
138
+ private readonly defaultConfig;
139
+ private readonly logger;
140
+ private readonly breakers;
141
+ constructor(defaultConfig?: CircuitBreakerConfig, logger?: Logger);
142
+ /**
143
+ * Get or create a circuit breaker by name
144
+ */
145
+ getOrCreate(name: string, config?: CircuitBreakerConfig): CircuitBreaker;
146
+ /**
147
+ * Get a circuit breaker by name (returns undefined if not exists)
148
+ */
149
+ get(name: string): CircuitBreaker | undefined;
150
+ /**
151
+ * Reset all circuit breakers
152
+ */
153
+ resetAll(): void;
154
+ /**
155
+ * Get stats for all circuit breakers
156
+ */
157
+ getAllStats(): Record<string, CircuitBreakerStats>;
158
+ }
159
+ //# sourceMappingURL=circuit-breaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/core/circuit-breaker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,299 @@
1
+ "use strict";
2
+ /**
3
+ * Circuit Breaker Implementation
4
+ *
5
+ * Provides resilience for external service calls by preventing cascading failures.
6
+ * Implements the circuit breaker pattern with three states:
7
+ * - CLOSED: Normal operation, requests flow through
8
+ * - OPEN: Failure threshold reached, requests are blocked
9
+ * - HALF_OPEN: Testing recovery, limited requests allowed
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.CircuitBreakerFactory = exports.CircuitBreaker = exports.CircuitOpenError = exports.CircuitState = void 0;
13
+ const error_handling_1 = require("./error-handling");
14
+ /**
15
+ * Circuit breaker states
16
+ */
17
+ var CircuitState;
18
+ (function (CircuitState) {
19
+ CircuitState["CLOSED"] = "closed";
20
+ CircuitState["OPEN"] = "open";
21
+ CircuitState["HALF_OPEN"] = "half_open";
22
+ })(CircuitState || (exports.CircuitState = CircuitState = {}));
23
+ /**
24
+ * Error thrown when circuit is open and blocking requests
25
+ */
26
+ class CircuitOpenError extends Error {
27
+ circuitName;
28
+ remainingCooldownMs;
29
+ state;
30
+ constructor(circuitName, remainingCooldownMs) {
31
+ super(`Circuit '${circuitName}' is open. Retry after ${Math.ceil(remainingCooldownMs)}ms`);
32
+ this.name = 'CircuitOpenError';
33
+ this.circuitName = circuitName;
34
+ this.remainingCooldownMs = remainingCooldownMs;
35
+ this.state = CircuitState.OPEN;
36
+ }
37
+ }
38
+ exports.CircuitOpenError = CircuitOpenError;
39
+ /**
40
+ * Circuit Breaker implementation
41
+ *
42
+ * Usage:
43
+ * ```typescript
44
+ * const breaker = new CircuitBreaker('embedding-api', { failureThreshold: 3 });
45
+ *
46
+ * try {
47
+ * const result = await breaker.execute(() => fetchFromAPI());
48
+ * } catch (error) {
49
+ * if (error instanceof CircuitOpenError) {
50
+ * // Circuit is open, handle gracefully
51
+ * }
52
+ * }
53
+ * ```
54
+ */
55
+ class CircuitBreaker {
56
+ name;
57
+ config;
58
+ logger;
59
+ state = CircuitState.CLOSED;
60
+ consecutiveFailures = 0;
61
+ totalFailures = 0;
62
+ totalSuccesses = 0;
63
+ lastFailureTime;
64
+ lastSuccessTime;
65
+ openedAt;
66
+ halfOpenAttempts = 0;
67
+ constructor(name, config, logger) {
68
+ this.name = name;
69
+ this.config = {
70
+ failureThreshold: config?.failureThreshold ?? 3,
71
+ cooldownPeriodMs: config?.cooldownPeriodMs ?? 30000,
72
+ halfOpenMaxAttempts: config?.halfOpenMaxAttempts ?? 1,
73
+ onStateChange: config?.onStateChange
74
+ };
75
+ this.logger = logger ?? new error_handling_1.ConsoleLogger('CircuitBreaker');
76
+ }
77
+ /**
78
+ * Execute an operation through the circuit breaker
79
+ * @throws CircuitOpenError if circuit is open
80
+ * @throws Original error if operation fails
81
+ */
82
+ async execute(operation) {
83
+ // Check if we should allow the request
84
+ if (!this.canExecute()) {
85
+ const remainingCooldown = this.getRemainingCooldown();
86
+ this.logger.warn(`Circuit '${this.name}' is open, blocking request`, {
87
+ remainingCooldownMs: remainingCooldown
88
+ });
89
+ throw new CircuitOpenError(this.name, remainingCooldown);
90
+ }
91
+ // Track half-open attempts
92
+ if (this.state === CircuitState.HALF_OPEN) {
93
+ this.halfOpenAttempts++;
94
+ this.logger.info(`Circuit '${this.name}' attempting request in half-open state`, {
95
+ attempt: this.halfOpenAttempts,
96
+ maxAttempts: this.config.halfOpenMaxAttempts
97
+ });
98
+ }
99
+ try {
100
+ const result = await operation();
101
+ this.recordSuccess();
102
+ return result;
103
+ }
104
+ catch (error) {
105
+ this.recordFailure(error);
106
+ throw error;
107
+ }
108
+ }
109
+ /**
110
+ * Record a successful operation
111
+ */
112
+ recordSuccess() {
113
+ this.consecutiveFailures = 0;
114
+ this.totalSuccesses++;
115
+ this.lastSuccessTime = new Date();
116
+ if (this.state === CircuitState.HALF_OPEN) {
117
+ // Success in half-open state closes the circuit
118
+ this.transitionTo(CircuitState.CLOSED);
119
+ this.halfOpenAttempts = 0;
120
+ this.logger.info(`Circuit '${this.name}' recovered, transitioning to closed`, {
121
+ totalSuccesses: this.totalSuccesses
122
+ });
123
+ }
124
+ }
125
+ /**
126
+ * Record a failed operation
127
+ */
128
+ recordFailure(error) {
129
+ this.consecutiveFailures++;
130
+ this.totalFailures++;
131
+ this.lastFailureTime = new Date();
132
+ this.logger.warn(`Circuit '${this.name}' recorded failure`, {
133
+ consecutiveFailures: this.consecutiveFailures,
134
+ threshold: this.config.failureThreshold,
135
+ error: error?.message
136
+ });
137
+ if (this.state === CircuitState.HALF_OPEN) {
138
+ // Failure in half-open state opens the circuit again
139
+ this.transitionTo(CircuitState.OPEN);
140
+ this.openedAt = new Date();
141
+ this.halfOpenAttempts = 0;
142
+ this.logger.warn(`Circuit '${this.name}' failed in half-open state, reopening`);
143
+ }
144
+ else if (this.state === CircuitState.CLOSED && this.consecutiveFailures >= this.config.failureThreshold) {
145
+ // Threshold reached, open the circuit
146
+ this.transitionTo(CircuitState.OPEN);
147
+ this.openedAt = new Date();
148
+ this.logger.error(`Circuit '${this.name}' opened after ${this.consecutiveFailures} consecutive failures`);
149
+ }
150
+ }
151
+ /**
152
+ * Reset the circuit breaker to initial state
153
+ */
154
+ reset() {
155
+ const previousState = this.state;
156
+ this.state = CircuitState.CLOSED;
157
+ this.consecutiveFailures = 0;
158
+ this.halfOpenAttempts = 0;
159
+ this.openedAt = undefined;
160
+ if (previousState !== CircuitState.CLOSED) {
161
+ this.logger.info(`Circuit '${this.name}' manually reset to closed`);
162
+ this.config.onStateChange?.(previousState, CircuitState.CLOSED, this.name);
163
+ }
164
+ }
165
+ /**
166
+ * Get the current circuit state
167
+ */
168
+ getState() {
169
+ // Check if we should transition from OPEN to HALF_OPEN
170
+ if (this.state === CircuitState.OPEN && this.shouldTransitionToHalfOpen()) {
171
+ this.transitionTo(CircuitState.HALF_OPEN);
172
+ this.halfOpenAttempts = 0;
173
+ this.logger.info(`Circuit '${this.name}' cooldown elapsed, transitioning to half-open`);
174
+ }
175
+ return this.state;
176
+ }
177
+ /**
178
+ * Get circuit breaker statistics
179
+ */
180
+ getStats() {
181
+ return {
182
+ state: this.getState(),
183
+ consecutiveFailures: this.consecutiveFailures,
184
+ totalFailures: this.totalFailures,
185
+ totalSuccesses: this.totalSuccesses,
186
+ lastFailureTime: this.lastFailureTime,
187
+ lastSuccessTime: this.lastSuccessTime,
188
+ openedAt: this.openedAt,
189
+ halfOpenAttempts: this.halfOpenAttempts
190
+ };
191
+ }
192
+ /**
193
+ * Check if circuit is currently open (blocking requests)
194
+ */
195
+ isOpen() {
196
+ return this.getState() === CircuitState.OPEN;
197
+ }
198
+ /**
199
+ * Get the circuit breaker name
200
+ */
201
+ getName() {
202
+ return this.name;
203
+ }
204
+ /**
205
+ * Check if a request can be executed
206
+ */
207
+ canExecute() {
208
+ const currentState = this.getState();
209
+ switch (currentState) {
210
+ case CircuitState.CLOSED:
211
+ return true;
212
+ case CircuitState.OPEN:
213
+ return false;
214
+ case CircuitState.HALF_OPEN:
215
+ // Allow limited requests in half-open state
216
+ return this.halfOpenAttempts < this.config.halfOpenMaxAttempts;
217
+ default:
218
+ return false;
219
+ }
220
+ }
221
+ /**
222
+ * Check if cooldown period has elapsed
223
+ */
224
+ shouldTransitionToHalfOpen() {
225
+ if (!this.openedAt) {
226
+ return false;
227
+ }
228
+ const elapsed = Date.now() - this.openedAt.getTime();
229
+ return elapsed >= this.config.cooldownPeriodMs;
230
+ }
231
+ /**
232
+ * Get remaining cooldown time in milliseconds
233
+ */
234
+ getRemainingCooldown() {
235
+ if (!this.openedAt) {
236
+ return 0;
237
+ }
238
+ const elapsed = Date.now() - this.openedAt.getTime();
239
+ const remaining = this.config.cooldownPeriodMs - elapsed;
240
+ return Math.max(0, remaining);
241
+ }
242
+ /**
243
+ * Transition to a new state
244
+ */
245
+ transitionTo(newState) {
246
+ const previousState = this.state;
247
+ this.state = newState;
248
+ this.config.onStateChange?.(previousState, newState, this.name);
249
+ }
250
+ }
251
+ exports.CircuitBreaker = CircuitBreaker;
252
+ /**
253
+ * Factory for creating circuit breakers with shared configuration
254
+ */
255
+ class CircuitBreakerFactory {
256
+ defaultConfig;
257
+ logger;
258
+ breakers = new Map();
259
+ constructor(defaultConfig, logger) {
260
+ this.defaultConfig = defaultConfig ?? {};
261
+ this.logger = logger ?? new error_handling_1.ConsoleLogger('CircuitBreakerFactory');
262
+ }
263
+ /**
264
+ * Get or create a circuit breaker by name
265
+ */
266
+ getOrCreate(name, config) {
267
+ let breaker = this.breakers.get(name);
268
+ if (!breaker) {
269
+ breaker = new CircuitBreaker(name, { ...this.defaultConfig, ...config }, this.logger);
270
+ this.breakers.set(name, breaker);
271
+ }
272
+ return breaker;
273
+ }
274
+ /**
275
+ * Get a circuit breaker by name (returns undefined if not exists)
276
+ */
277
+ get(name) {
278
+ return this.breakers.get(name);
279
+ }
280
+ /**
281
+ * Reset all circuit breakers
282
+ */
283
+ resetAll() {
284
+ for (const breaker of this.breakers.values()) {
285
+ breaker.reset();
286
+ }
287
+ }
288
+ /**
289
+ * Get stats for all circuit breakers
290
+ */
291
+ getAllStats() {
292
+ const stats = {};
293
+ for (const [name, breaker] of this.breakers) {
294
+ stats[name] = breaker.getStats();
295
+ }
296
+ return stats;
297
+ }
298
+ }
299
+ exports.CircuitBreakerFactory = CircuitBreakerFactory;
@@ -4,6 +4,7 @@
4
4
  * Optional semantic search enhancement for pattern matching.
5
5
  * Gracefully falls back to keyword-only search when embedding providers are not available.
6
6
  */
7
+ import { CircuitBreakerStats } from './circuit-breaker';
7
8
  /**
8
9
  * Supported embedding providers - single source of truth
9
10
  */
@@ -87,5 +88,10 @@ export declare class EmbeddingService {
87
88
  suggestedResources: string[];
88
89
  rationale: string;
89
90
  }): string;
91
+ /**
92
+ * Get circuit breaker statistics for monitoring
93
+ * Returns null if embedding service is not available
94
+ */
95
+ getCircuitBreakerStats(): CircuitBreakerStats | null;
90
96
  }
91
97
  //# sourceMappingURL=embedding-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"embedding-service.d.ts","sourceRoot":"","sources":["../../src/core/embedding-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH;;GAEG;AACH,eAAO,MAAM,mBAAmB,iDAAkD,CAAC;AACnF,MAAM,MAAM,qBAAqB,GAAG,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACnD,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzD,WAAW,IAAI,OAAO,CAAC;IACvB,aAAa,IAAI,MAAM,CAAC;IACxB,QAAQ,IAAI,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAC/D,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,aAAa,CAAM;gBAEf,MAAM,EAAE,eAAe,GAAG;QAAE,QAAQ,EAAE,qBAAqB,CAAA;KAAE;IAgEnE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAiDlD,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IA8D9D,WAAW,IAAI,OAAO;IAItB,aAAa,IAAI,MAAM;IAIvB,QAAQ,IAAI,MAAM;IAIlB,eAAe,IAAI,MAAM;CAG1B;AA0BD;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAA2B;gBAE/B,MAAM,GAAE,eAAoB;IAKxC;;;OAGG;IACG,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAaxD;;;OAGG;IACG,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAc9D;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,SAAS,IAAI;QACX,SAAS,EAAE,OAAO,CAAC;QACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IA4BD;;OAEG;IACH,uBAAuB,CAAC,OAAO,EAAE;QAC/B,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,kBAAkB,EAAE,MAAM,EAAE,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,MAAM;CASX"}
1
+ {"version":3,"file":"embedding-service.d.ts","sourceRoot":"","sources":["../../src/core/embedding-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAkB,mBAAmB,EAAoB,MAAM,mBAAmB,CAAC;AAc1F;;GAEG;AACH,eAAO,MAAM,mBAAmB,iDAAkD,CAAC;AACnF,MAAM,MAAM,qBAAqB,GAAG,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACnD,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzD,WAAW,IAAI,OAAO,CAAC;IACvB,aAAa,IAAI,MAAM,CAAC;IACxB,QAAQ,IAAI,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAC/D,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,aAAa,CAAM;gBAEf,MAAM,EAAE,eAAe,GAAG;QAAE,QAAQ,EAAE,qBAAqB,CAAA;KAAE;IAgEnE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAwDlD,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAqE9D,WAAW,IAAI,OAAO;IAItB,aAAa,IAAI,MAAM;IAIvB,QAAQ,IAAI,MAAM;IAIlB,eAAe,IAAI,MAAM;CAG1B;AA0BD;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAA2B;gBAE/B,MAAM,GAAE,eAAoB;IAKxC;;;OAGG;IACG,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAaxD;;;OAGG;IACG,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAc9D;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,SAAS,IAAI;QACX,SAAS,EAAE,OAAO,CAAC;QACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IA4BD;;OAEG;IACH,uBAAuB,CAAC,OAAO,EAAE;QAC/B,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,kBAAkB,EAAE,MAAM,EAAE,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,MAAM;IAUV;;;OAGG;IACH,sBAAsB,IAAI,mBAAmB,GAAG,IAAI;CAGrD"}
@@ -11,7 +11,18 @@ const amazon_bedrock_1 = require("@ai-sdk/amazon-bedrock");
11
11
  const google_1 = require("@ai-sdk/google");
12
12
  const openai_1 = require("@ai-sdk/openai");
13
13
  const ai_1 = require("ai");
14
+ const circuit_breaker_1 = require("./circuit-breaker");
14
15
  const tracing_1 = require("./tracing");
16
+ /**
17
+ * Module-level circuit breaker for embedding API calls.
18
+ * Shared across all EmbeddingService instances since they hit the same API.
19
+ * Opens after 3 consecutive failures, blocks for 30s before testing recovery.
20
+ */
21
+ const embeddingCircuitBreaker = new circuit_breaker_1.CircuitBreaker('embedding-api', {
22
+ failureThreshold: 3, // Open after 3 consecutive failures
23
+ cooldownPeriodMs: 30000, // 30s cooldown before half-open
24
+ halfOpenMaxAttempts: 1 // Allow 1 test request in half-open
25
+ });
15
26
  /**
16
27
  * Supported embedding providers - single source of truth
17
28
  */
@@ -102,23 +113,30 @@ class VercelEmbeddingProvider {
102
113
  operation: 'embeddings'
103
114
  }, async () => {
104
115
  try {
105
- const embedOptions = {
106
- model: this.modelInstance,
107
- value: text.trim()
108
- };
109
- // Add Google-specific options
110
- if (this.providerType === 'google') {
111
- embedOptions.providerOptions = {
112
- google: {
113
- outputDimensionality: this.dimensions,
114
- taskType: 'SEMANTIC_SIMILARITY'
115
- }
116
+ // Execute through circuit breaker to prevent cascading failures
117
+ return await embeddingCircuitBreaker.execute(async () => {
118
+ const embedOptions = {
119
+ model: this.modelInstance,
120
+ value: text.trim()
116
121
  };
117
- }
118
- const result = await (0, ai_1.embed)(embedOptions);
119
- return result.embedding;
122
+ // Add Google-specific options
123
+ if (this.providerType === 'google') {
124
+ embedOptions.providerOptions = {
125
+ google: {
126
+ outputDimensionality: this.dimensions,
127
+ taskType: 'SEMANTIC_SIMILARITY'
128
+ }
129
+ };
130
+ }
131
+ const result = await (0, ai_1.embed)(embedOptions);
132
+ return result.embedding;
133
+ });
120
134
  }
121
135
  catch (error) {
136
+ // Convert CircuitOpenError to descriptive message
137
+ if (error instanceof circuit_breaker_1.CircuitOpenError) {
138
+ throw new Error(`Embedding API circuit open: ${error.message}`);
139
+ }
122
140
  if (error instanceof Error) {
123
141
  throw new Error(`${this.providerType} embedding failed: ${error.message}`);
124
142
  }
@@ -149,25 +167,32 @@ class VercelEmbeddingProvider {
149
167
  operation: 'embeddings'
150
168
  }, async () => {
151
169
  try {
152
- const results = await Promise.all(validTexts.map(text => {
153
- const embedOptions = {
154
- model: this.modelInstance,
155
- value: text
156
- };
157
- // Add Google-specific options
158
- if (this.providerType === 'google') {
159
- embedOptions.providerOptions = {
160
- google: {
161
- outputDimensionality: this.dimensions,
162
- taskType: 'SEMANTIC_SIMILARITY'
163
- }
170
+ // Execute through circuit breaker to prevent cascading failures
171
+ return await embeddingCircuitBreaker.execute(async () => {
172
+ const results = await Promise.all(validTexts.map(text => {
173
+ const embedOptions = {
174
+ model: this.modelInstance,
175
+ value: text
164
176
  };
165
- }
166
- return (0, ai_1.embed)(embedOptions);
167
- }));
168
- return results.map(result => result.embedding);
177
+ // Add Google-specific options
178
+ if (this.providerType === 'google') {
179
+ embedOptions.providerOptions = {
180
+ google: {
181
+ outputDimensionality: this.dimensions,
182
+ taskType: 'SEMANTIC_SIMILARITY'
183
+ }
184
+ };
185
+ }
186
+ return (0, ai_1.embed)(embedOptions);
187
+ }));
188
+ return results.map(result => result.embedding);
189
+ });
169
190
  }
170
191
  catch (error) {
192
+ // Convert CircuitOpenError to descriptive message
193
+ if (error instanceof circuit_breaker_1.CircuitOpenError) {
194
+ throw new Error(`Embedding API circuit open: ${error.message}`);
195
+ }
171
196
  if (error instanceof Error) {
172
197
  throw new Error(`${this.providerType} batch embedding failed: ${error.message}`);
173
198
  }
@@ -308,5 +333,12 @@ class EmbeddingService {
308
333
  ...pattern.suggestedResources.map(r => `kubernetes ${r.toLowerCase()}`)
309
334
  ].join(' ').trim();
310
335
  }
336
+ /**
337
+ * Get circuit breaker statistics for monitoring
338
+ * Returns null if embedding service is not available
339
+ */
340
+ getCircuitBreakerStats() {
341
+ return embeddingCircuitBreaker.getStats();
342
+ }
311
343
  }
312
344
  exports.EmbeddingService = EmbeddingService;
@@ -53,5 +53,6 @@ export { PolicyVectorService, PolicySearchOptions, PolicySearchResult } from './
53
53
  export { CapabilityVectorService, ResourceCapability, CapabilitySearchOptions } from './capability-vector-service';
54
54
  export { EmbeddingService, EmbeddingConfig, EmbeddingProvider, VercelEmbeddingProvider } from './embedding-service';
55
55
  export { AgentDisplayOptions, buildAgentDisplayBlock } from './agent-display';
56
+ export { CircuitBreaker, CircuitBreakerFactory, CircuitBreakerConfig, CircuitBreakerStats, CircuitState, CircuitOpenError } from './circuit-breaker';
56
57
  export default DotAI;
57
58
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  * Shared intelligence for both CLI and MCP interfaces
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.buildAgentDisplayBlock = exports.VercelEmbeddingProvider = exports.EmbeddingService = exports.CapabilityVectorService = exports.PolicyVectorService = exports.PatternVectorService = exports.BaseVectorService = exports.VectorDBService = exports.deserializePattern = exports.serializePattern = exports.createPattern = exports.validatePattern = exports.ResourceRecommender = exports.ManifestValidator = exports.SchemaParser = exports.AIProviderFactory = exports.createAIProvider = exports.WorkflowEngine = exports.MemorySystem = exports.KubernetesDiscovery = exports.DotAI = void 0;
8
+ exports.CircuitOpenError = exports.CircuitState = exports.CircuitBreakerFactory = exports.CircuitBreaker = exports.buildAgentDisplayBlock = exports.VercelEmbeddingProvider = exports.EmbeddingService = exports.CapabilityVectorService = exports.PolicyVectorService = exports.PatternVectorService = exports.BaseVectorService = exports.VectorDBService = exports.deserializePattern = exports.serializePattern = exports.createPattern = exports.validatePattern = exports.ResourceRecommender = exports.ManifestValidator = exports.SchemaParser = exports.AIProviderFactory = exports.createAIProvider = exports.WorkflowEngine = exports.MemorySystem = exports.KubernetesDiscovery = exports.DotAI = void 0;
9
9
  const discovery_1 = require("./discovery");
10
10
  const memory_1 = require("./memory");
11
11
  const workflow_1 = require("./workflow");
@@ -153,5 +153,10 @@ Object.defineProperty(exports, "EmbeddingService", { enumerable: true, get: func
153
153
  Object.defineProperty(exports, "VercelEmbeddingProvider", { enumerable: true, get: function () { return embedding_service_1.VercelEmbeddingProvider; } });
154
154
  var agent_display_1 = require("./agent-display");
155
155
  Object.defineProperty(exports, "buildAgentDisplayBlock", { enumerable: true, get: function () { return agent_display_1.buildAgentDisplayBlock; } });
156
+ var circuit_breaker_1 = require("./circuit-breaker");
157
+ Object.defineProperty(exports, "CircuitBreaker", { enumerable: true, get: function () { return circuit_breaker_1.CircuitBreaker; } });
158
+ Object.defineProperty(exports, "CircuitBreakerFactory", { enumerable: true, get: function () { return circuit_breaker_1.CircuitBreakerFactory; } });
159
+ Object.defineProperty(exports, "CircuitState", { enumerable: true, get: function () { return circuit_breaker_1.CircuitState; } });
160
+ Object.defineProperty(exports, "CircuitOpenError", { enumerable: true, get: function () { return circuit_breaker_1.CircuitOpenError; } });
156
161
  // Default export
157
162
  exports.default = DotAI;
@@ -1 +1 @@
1
- {"version":3,"file":"mermaid-tools.d.ts","sourceRoot":"","sources":["../../src/core/mermaid-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAWjD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,MA0BnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,EAEjC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,GAAG,GACT,OAAO,CAAC,GAAG,CAAC,CAoDd"}
1
+ {"version":3,"file":"mermaid-tools.d.ts","sourceRoot":"","sources":["../../src/core/mermaid-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AASjD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,MA0BnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,EAEjC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,GAAG,GACT,OAAO,CAAC,GAAG,CAAC,CAoDd"}
@@ -17,9 +17,7 @@ const mermaid_1 = __importDefault(require("mermaid"));
17
17
  // Initialize mermaid with strict validation
18
18
  mermaid_1.default.initialize({
19
19
  securityLevel: 'strict',
20
- startOnLoad: false,
21
- // Disable rendering, we only want parsing/validation
22
- suppressErrorRendering: true
20
+ startOnLoad: false
23
21
  });
24
22
  /**
25
23
  * Tool: validate_mermaid
@@ -1 +1 @@
1
- {"version":3,"file":"unified-creation-session.d.ts","sourceRoot":"","sources":["../../src/core/unified-creation-session.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAKlD,OAAO,EACL,sBAAsB,EAEtB,2BAA2B,EAC3B,iCAAiC,EACjC,UAAU,EAIX,MAAM,0BAA0B,CAAC;AAKlC,qBAAa,6BAA6B;IACxC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,cAAc,CAAoD;gBAE9D,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,mBAAmB;IAMnE;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,sBAAsB;IAWhD;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,sBAAsB,GAAG,IAAI;IAIzE;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,sBAAsB;IA2GxF;;OAEG;IACG,mBAAmB,CAAC,OAAO,EAAE,sBAAsB,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,2BAA2B,GAAG,iCAAiC,CAAC;IAoGhJ;;OAEG;YACW,4BAA4B;IA4C1C;;OAEG;YACW,gCAAgC;IAyC9C;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA6E1B;;OAEG;YACW,gBAAgB;IAsF9B;;OAEG;YACW,4BAA4B;IAoH1C;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;YACW,qBAAqB;IAqCnC;;OAEG;YACW,mBAAmB;IAyLjC;;OAEG;YACW,uBAAuB;IAsGrC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAU5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAY/B"}
1
+ {"version":3,"file":"unified-creation-session.d.ts","sourceRoot":"","sources":["../../src/core/unified-creation-session.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAKlD,OAAO,EACL,sBAAsB,EAEtB,2BAA2B,EAC3B,iCAAiC,EACjC,UAAU,EAIX,MAAM,0BAA0B,CAAC;AAKlC,qBAAa,6BAA6B;IACxC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,cAAc,CAAoD;gBAE9D,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,mBAAmB;IAMnE;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,sBAAsB;IAWhD;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,sBAAsB,GAAG,IAAI;IAIzE;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,sBAAsB;IAiHxF;;OAEG;IACG,mBAAmB,CAAC,OAAO,EAAE,sBAAsB,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,2BAA2B,GAAG,iCAAiC,CAAC;IAoGhJ;;OAEG;YACW,4BAA4B;IA4C1C;;OAEG;YACW,gCAAgC;IAyC9C;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA6E1B;;OAEG;YACW,gBAAgB;IAsF9B;;OAEG;YACW,4BAA4B;IAoH1C;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;YACW,qBAAqB;IAqCnC;;OAEG;YACW,mBAAmB;IAyLjC;;OAEG;YACW,uBAAuB;IAsGrC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAU5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAY/B"}
@@ -141,6 +141,13 @@ class UnifiedCreationSessionManager {
141
141
  const namespaces = scopeChoice.replace('exclude:', '').split(',').map(ns => ns.trim()).filter(ns => ns.length > 0);
142
142
  session.data.namespaceScope = { type: 'exclude', namespaces };
143
143
  }
144
+ else {
145
+ // Treat any other input as namespace names to include (e.g., "policy-test" or "ns1, ns2")
146
+ const namespaces = scopeChoice.split(',').map(ns => ns.trim()).filter(ns => ns.length > 0);
147
+ if (namespaces.length > 0) {
148
+ session.data.namespaceScope = { type: 'include', namespaces };
149
+ }
150
+ }
144
151
  session.data.currentStep = (0, unified_creation_types_1.getNextStep)('namespace-scope', this.config);
145
152
  break;
146
153
  }
@@ -43,6 +43,11 @@ export declare class VectorDBService {
43
43
  * Handles conflict errors gracefully (collection already exists from race condition or restart)
44
44
  */
45
45
  private createCollection;
46
+ /**
47
+ * Ensure text index exists on searchText field for efficient keyword search
48
+ * This is idempotent - safe to call multiple times
49
+ */
50
+ private ensureTextIndex;
46
51
  /**
47
52
  * Store a document with optional vector
48
53
  */
@@ -53,6 +58,7 @@ export declare class VectorDBService {
53
58
  searchSimilar(vector: number[], options?: SearchOptions): Promise<SearchResult[]>;
54
59
  /**
55
60
  * Search for documents using payload filtering (keyword search)
61
+ * Uses Qdrant's native text index for efficient server-side filtering
56
62
  */
57
63
  searchByKeywords(keywords: string[], options?: SearchOptions): Promise<SearchResult[]>;
58
64
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"vector-db-service.d.ts","sourceRoot":"","sources":["../../src/core/vector-db-service.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgDH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAS;gBAEnB,MAAM,GAAE,cAAmB;IAsBvC,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,sBAAsB;IAM9B;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAa1C;;OAEG;IACG,oBAAoB,CAAC,UAAU,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDnE;;;OAGG;YACW,gBAAgB;IAgC9B;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC7D;;OAEG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC;IAuC1B;;OAEG;IACG,gBAAgB,CACpB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC;IA0F1B;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqC7D;;OAEG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B/C;;;;OAIG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCzC;;;;;OAKG;IACG,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,GAAE,MAAY,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAuCnF;;;;OAIG;IACG,eAAe,CAAC,KAAK,GAAE,MAAc,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAgDvE;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC;IAqBvC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAsBrC;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,SAAS,IAAI,cAAc;CAG5B"}
1
+ {"version":3,"file":"vector-db-service.d.ts","sourceRoot":"","sources":["../../src/core/vector-db-service.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgDH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAS;gBAEnB,MAAM,GAAE,cAAmB;IAuBvC,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,sBAAsB;IAM9B;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAa1C;;OAEG;IACG,oBAAoB,CAAC,UAAU,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAoDnE;;;OAGG;YACW,gBAAgB;IAmC9B;;;OAGG;YACW,eAAe;IAwC7B;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC7D;;OAEG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC;IAuC1B;;;OAGG;IACG,gBAAgB,CACpB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC;IA8H1B;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqC7D;;OAEG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B/C;;;;OAIG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCzC;;;;;OAKG;IACG,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,GAAE,MAAY,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAuCnF;;;;OAIG;IACG,eAAe,CAAC,KAAK,GAAE,MAAc,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAgDvE;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC;IAqBvC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAsBrC;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,SAAS,IAAI,cAAc;CAG5B"}
@@ -44,8 +44,8 @@ class QdrantSemaphore {
44
44
  }
45
45
  }
46
46
  }
47
- // Limit to 20 concurrent Qdrant operations
48
- const qdrantSemaphore = new QdrantSemaphore(20);
47
+ // Limit concurrent Qdrant bulk operations (scroll, getAllDocuments)
48
+ const qdrantSemaphore = new QdrantSemaphore(100);
49
49
  class VectorDBService {
50
50
  client = null;
51
51
  config;
@@ -64,7 +64,8 @@ class VectorDBService {
64
64
  if (this.shouldInitializeClient()) {
65
65
  this.client = new js_client_rest_1.QdrantClient({
66
66
  url: this.config.url,
67
- apiKey: this.config.apiKey
67
+ apiKey: this.config.apiKey,
68
+ maxConnections: 100, // HTTP keep-alive pool for connection reuse
68
69
  });
69
70
  }
70
71
  }
@@ -125,6 +126,10 @@ class VectorDBService {
125
126
  await this.client.deleteCollection(this.collectionName);
126
127
  await this.createCollection(vectorSize);
127
128
  }
129
+ else {
130
+ // Ensure text index exists for existing collections (transparent upgrade)
131
+ await this.ensureTextIndex();
132
+ }
128
133
  }
129
134
  catch (error) {
130
135
  // If we can't get collection info, assume it's corrupted and recreate
@@ -158,11 +163,12 @@ class VectorDBService {
158
163
  distance: 'Cosine',
159
164
  on_disk: true // Enable on-disk storage for better performance with large collections
160
165
  },
161
- // Enable payload indexing for better keyword search performance
162
166
  optimizers_config: {
163
167
  default_segment_number: 2
164
168
  }
165
169
  });
170
+ // Create text index on searchText field for efficient keyword search
171
+ await this.ensureTextIndex();
166
172
  }
167
173
  catch (error) {
168
174
  // Handle race condition where collection was created between check and create
@@ -173,11 +179,53 @@ class VectorDBService {
173
179
  if (process.env.DEBUG_DOT_AI) {
174
180
  console.debug(`Collection ${this.collectionName} already exists, skipping creation`);
175
181
  }
182
+ await this.ensureTextIndex();
176
183
  return;
177
184
  }
178
185
  throw error;
179
186
  }
180
187
  }
188
+ /**
189
+ * Ensure text index exists on searchText field for efficient keyword search
190
+ * This is idempotent - safe to call multiple times
191
+ */
192
+ async ensureTextIndex() {
193
+ if (!this.client) {
194
+ return;
195
+ }
196
+ try {
197
+ // Check if index already exists
198
+ const collectionInfo = await this.client.getCollection(this.collectionName);
199
+ const payloadSchema = collectionInfo.payload_schema || {};
200
+ // Check if searchText already has a text index
201
+ const searchTextIndex = payloadSchema['searchText'];
202
+ if (searchTextIndex && searchTextIndex.data_type === 'text') {
203
+ return; // Index already exists
204
+ }
205
+ // Create text index on searchText field
206
+ await this.client.createPayloadIndex(this.collectionName, {
207
+ field_name: 'searchText',
208
+ field_schema: 'text',
209
+ });
210
+ if (process.env.DEBUG_DOT_AI) {
211
+ console.debug(`Created text index on searchText field for collection ${this.collectionName}`);
212
+ }
213
+ }
214
+ catch (error) {
215
+ const errorMessage = error instanceof Error ? error.message : String(error);
216
+ const isAlreadyExists = errorMessage.toLowerCase().includes('already exists') ||
217
+ errorMessage.toLowerCase().includes('conflict');
218
+ if (isAlreadyExists) {
219
+ if (process.env.DEBUG_DOT_AI) {
220
+ console.debug(`Text index already exists for collection ${this.collectionName}`);
221
+ }
222
+ }
223
+ else {
224
+ // Unexpected error - log at warn level for visibility
225
+ console.warn(`Failed to create text index on ${this.collectionName}: ${errorMessage}`);
226
+ }
227
+ }
228
+ }
181
229
  /**
182
230
  * Store a document with optional vector
183
231
  */
@@ -250,12 +298,16 @@ class VectorDBService {
250
298
  }
251
299
  /**
252
300
  * Search for documents using payload filtering (keyword search)
301
+ * Uses Qdrant's native text index for efficient server-side filtering
253
302
  */
254
303
  async searchByKeywords(keywords, options = {}) {
255
304
  if (!this.client) {
256
305
  throw new Error('Vector DB client not initialized');
257
306
  }
258
307
  const limit = options.limit || 10;
308
+ if (keywords.length === 0) {
309
+ return [];
310
+ }
259
311
  return (0, qdrant_tracing_1.withQdrantTracing)({
260
312
  operation: 'vector.search_keywords',
261
313
  collectionName: this.collectionName,
@@ -264,15 +316,50 @@ class VectorDBService {
264
316
  serverUrl: this.config.url
265
317
  }, async () => {
266
318
  try {
267
- // Fallback to JavaScript-based filtering due to Qdrant filter syntax issues
268
- // Get all documents and filter in JavaScript for keyword matching
319
+ // Build Qdrant filter for text search
320
+ // Use "should" (OR) to match any keyword in searchText or triggers
321
+ const keywordConditions = [];
322
+ for (const keyword of keywords) {
323
+ // Text match on searchText field (uses text index)
324
+ keywordConditions.push({
325
+ key: 'searchText',
326
+ match: { text: keyword }
327
+ });
328
+ // Match on triggers array (for patterns/policies)
329
+ keywordConditions.push({
330
+ key: 'triggers',
331
+ match: { any: [keyword, keyword.toLowerCase()] }
332
+ });
333
+ }
334
+ // Combine keyword conditions with any user-provided filter
335
+ const filter = {
336
+ should: keywordConditions
337
+ };
338
+ // If user provided additional filters, merge them properly
339
+ if (options.filter) {
340
+ // Merge must conditions
341
+ if (options.filter.must) {
342
+ filter.must = Array.isArray(options.filter.must)
343
+ ? options.filter.must
344
+ : [options.filter.must];
345
+ }
346
+ // Preserve must_not conditions
347
+ if (options.filter.must_not) {
348
+ filter.must_not = options.filter.must_not;
349
+ }
350
+ // If user filter has should conditions, wrap in must to AND with keyword should
351
+ if (options.filter.should) {
352
+ filter.must = [...(filter.must || []), { should: options.filter.should }];
353
+ }
354
+ }
355
+ // Use scroll with native Qdrant filtering - much faster than client-side
269
356
  const scrollResult = await this.client.scroll(this.collectionName, {
270
- limit: 1000, // Get all documents for filtering
357
+ limit: limit * 10, // Get more candidates for scoring, but not 1000
271
358
  with_payload: true,
272
359
  with_vector: false,
273
- ...(options.filter && { filter: options.filter })
360
+ filter
274
361
  });
275
- // Filter documents by checking if any keyword matches searchText or triggers
362
+ // Score the filtered results (small set now)
276
363
  const scoredPoints = scrollResult.points
277
364
  .map(point => {
278
365
  if (!point.payload)
@@ -286,35 +373,27 @@ class VectorDBService {
286
373
  let exactMatch = false;
287
374
  for (const keyword of keywords) {
288
375
  const kw = keyword.toLowerCase();
289
- // Check searchText (name, kind, namespace, labels, etc.)
376
+ // Check searchText
290
377
  if (searchText.includes(kw)) {
291
378
  matchCount++;
292
- // Bonus for exact word match (surrounded by spaces/punctuation)
293
379
  const wordPattern = new RegExp(`\\b${escapeRegExp(kw)}\\b`, 'i');
294
380
  if (wordPattern.test(searchText)) {
295
381
  exactMatch = true;
296
382
  }
297
383
  }
298
- // Check triggers (for patterns/policies)
384
+ // Check triggers
299
385
  if (triggers.some(t => t.includes(kw) || kw.includes(t))) {
300
386
  matchCount++;
301
387
  }
302
388
  }
303
389
  if (matchCount === 0)
304
390
  return null;
305
- // Score based on match quality
306
- // - Base score from match ratio
307
- // - Bonus for exact word matches
308
391
  const baseScore = matchCount / keywords.length;
309
392
  const score = exactMatch ? Math.min(1.0, baseScore + 0.3) : baseScore;
310
- return {
311
- point,
312
- score
313
- };
393
+ return { point, score };
314
394
  })
315
395
  .filter((item) => item !== null)
316
396
  .sort((a, b) => b.score - a.score);
317
- // Apply limit after filtering
318
397
  const limitedResults = scoredPoints.slice(0, limit);
319
398
  return limitedResults.map(({ point, score }) => ({
320
399
  id: point.id.toString(),
@@ -10,9 +10,9 @@ export declare const ANSWERQUESTION_TOOL_INPUT_SCHEMA: {
10
10
  solutionId: z.ZodString;
11
11
  stage: z.ZodEnum<{
12
12
  required: "required";
13
+ open: "open";
13
14
  basic: "basic";
14
15
  advanced: "advanced";
15
- open: "open";
16
16
  }>;
17
17
  answers: z.ZodRecord<z.ZodString, z.ZodAny>;
18
18
  interaction_id: z.ZodOptional<z.ZodString>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vfarcic/dot-ai",
3
- "version": "0.194.0",
3
+ "version": "0.195.0",
4
4
  "description": "AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance",
5
5
  "mcpName": "io.github.vfarcic/dot-ai",
6
6
  "main": "dist/index.js",
@@ -114,7 +114,7 @@
114
114
  "@qdrant/js-client-rest": "^1.15.0",
115
115
  "ai": "^5.0.60",
116
116
  "handlebars": "^4.7.8",
117
- "mermaid": "^11.12.2",
117
+ "mermaid": "^10.9.5",
118
118
  "posthog-node": "^5.23.0",
119
119
  "yaml": "^2.8.0",
120
120
  "zod-to-json-schema": "^3.24.6"