@cogitator-ai/core 0.1.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/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/__tests__/agent.test.d.ts +2 -0
- package/dist/__tests__/agent.test.d.ts.map +1 -0
- package/dist/__tests__/agent.test.js +91 -0
- package/dist/__tests__/agent.test.js.map +1 -0
- package/dist/__tests__/base64.test.d.ts +2 -0
- package/dist/__tests__/base64.test.d.ts.map +1 -0
- package/dist/__tests__/base64.test.js +62 -0
- package/dist/__tests__/base64.test.js.map +1 -0
- package/dist/__tests__/calculator.test.d.ts +2 -0
- package/dist/__tests__/calculator.test.d.ts.map +1 -0
- package/dist/__tests__/calculator.test.js +146 -0
- package/dist/__tests__/calculator.test.js.map +1 -0
- package/dist/__tests__/cogitator-memory.test.d.ts +2 -0
- package/dist/__tests__/cogitator-memory.test.d.ts.map +1 -0
- package/dist/__tests__/cogitator-memory.test.js +176 -0
- package/dist/__tests__/cogitator-memory.test.js.map +1 -0
- package/dist/__tests__/datetime.test.d.ts +2 -0
- package/dist/__tests__/datetime.test.d.ts.map +1 -0
- package/dist/__tests__/datetime.test.js +87 -0
- package/dist/__tests__/datetime.test.js.map +1 -0
- package/dist/__tests__/exec.test.d.ts +2 -0
- package/dist/__tests__/exec.test.d.ts.map +1 -0
- package/dist/__tests__/exec.test.js +59 -0
- package/dist/__tests__/exec.test.js.map +1 -0
- package/dist/__tests__/filesystem.test.d.ts +2 -0
- package/dist/__tests__/filesystem.test.d.ts.map +1 -0
- package/dist/__tests__/filesystem.test.js +148 -0
- package/dist/__tests__/filesystem.test.js.map +1 -0
- package/dist/__tests__/google-backend.test.d.ts +5 -0
- package/dist/__tests__/google-backend.test.d.ts.map +1 -0
- package/dist/__tests__/google-backend.test.js +429 -0
- package/dist/__tests__/google-backend.test.js.map +1 -0
- package/dist/__tests__/hash.test.d.ts +2 -0
- package/dist/__tests__/hash.test.d.ts.map +1 -0
- package/dist/__tests__/hash.test.js +50 -0
- package/dist/__tests__/hash.test.js.map +1 -0
- package/dist/__tests__/http.test.d.ts +2 -0
- package/dist/__tests__/http.test.d.ts.map +1 -0
- package/dist/__tests__/http.test.js +64 -0
- package/dist/__tests__/http.test.js.map +1 -0
- package/dist/__tests__/json.test.d.ts +2 -0
- package/dist/__tests__/json.test.d.ts.map +1 -0
- package/dist/__tests__/json.test.js +65 -0
- package/dist/__tests__/json.test.js.map +1 -0
- package/dist/__tests__/logger.test.d.ts +2 -0
- package/dist/__tests__/logger.test.d.ts.map +1 -0
- package/dist/__tests__/logger.test.js +186 -0
- package/dist/__tests__/logger.test.js.map +1 -0
- package/dist/__tests__/random.test.d.ts +2 -0
- package/dist/__tests__/random.test.d.ts.map +1 -0
- package/dist/__tests__/random.test.js +81 -0
- package/dist/__tests__/random.test.js.map +1 -0
- package/dist/__tests__/regex.test.d.ts +2 -0
- package/dist/__tests__/regex.test.d.ts.map +1 -0
- package/dist/__tests__/regex.test.js +75 -0
- package/dist/__tests__/regex.test.js.map +1 -0
- package/dist/__tests__/registry.test.d.ts +2 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.js +102 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/sleep.test.d.ts +2 -0
- package/dist/__tests__/sleep.test.d.ts.map +1 -0
- package/dist/__tests__/sleep.test.js +29 -0
- package/dist/__tests__/sleep.test.js.map +1 -0
- package/dist/__tests__/tool.test.d.ts +2 -0
- package/dist/__tests__/tool.test.d.ts.map +1 -0
- package/dist/__tests__/tool.test.js +103 -0
- package/dist/__tests__/tool.test.js.map +1 -0
- package/dist/__tests__/uuid.test.d.ts +2 -0
- package/dist/__tests__/uuid.test.d.ts.map +1 -0
- package/dist/__tests__/uuid.test.js +37 -0
- package/dist/__tests__/uuid.test.js.map +1 -0
- package/dist/agent.d.ts +15 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +35 -0
- package/dist/agent.js.map +1 -0
- package/dist/cogitator.d.ts +66 -0
- package/dist/cogitator.d.ts.map +1 -0
- package/dist/cogitator.js +538 -0
- package/dist/cogitator.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/anthropic.d.ts +19 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +188 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/base.d.ts +11 -0
- package/dist/llm/base.d.ts.map +1 -0
- package/dist/llm/base.js +9 -0
- package/dist/llm/base.js.map +1 -0
- package/dist/llm/google.d.ts +32 -0
- package/dist/llm/google.d.ts.map +1 -0
- package/dist/llm/google.js +282 -0
- package/dist/llm/google.js.map +1 -0
- package/dist/llm/index.d.ts +22 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +67 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/ollama.d.ts +20 -0
- package/dist/llm/ollama.d.ts.map +1 -0
- package/dist/llm/ollama.js +134 -0
- package/dist/llm/ollama.js.map +1 -0
- package/dist/llm/openai.d.ts +21 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +154 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/logger.d.ts +48 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +118 -0
- package/dist/logger.js.map +1 -0
- package/dist/registry.d.ts +15 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +31 -0
- package/dist/registry.js.map +1 -0
- package/dist/tool.d.ts +13 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +44 -0
- package/dist/tool.js.map +1 -0
- package/dist/tools/base64.d.ts +23 -0
- package/dist/tools/base64.d.ts.map +1 -0
- package/dist/tools/base64.js +53 -0
- package/dist/tools/base64.js.map +1 -0
- package/dist/tools/calculator.d.ts +15 -0
- package/dist/tools/calculator.d.ts.map +1 -0
- package/dist/tools/calculator.js +193 -0
- package/dist/tools/calculator.js.map +1 -0
- package/dist/tools/datetime.d.ts +23 -0
- package/dist/tools/datetime.d.ts.map +1 -0
- package/dist/tools/datetime.js +95 -0
- package/dist/tools/datetime.js.map +1 -0
- package/dist/tools/exec.d.ts +46 -0
- package/dist/tools/exec.d.ts.map +1 -0
- package/dist/tools/exec.js +87 -0
- package/dist/tools/exec.js.map +1 -0
- package/dist/tools/filesystem.d.ts +100 -0
- package/dist/tools/filesystem.d.ts.map +1 -0
- package/dist/tools/filesystem.js +186 -0
- package/dist/tools/filesystem.js.map +1 -0
- package/dist/tools/hash.d.ts +19 -0
- package/dist/tools/hash.d.ts.map +1 -0
- package/dist/tools/hash.js +32 -0
- package/dist/tools/hash.js.map +1 -0
- package/dist/tools/http.d.ts +29 -0
- package/dist/tools/http.d.ts.map +1 -0
- package/dist/tools/http.js +82 -0
- package/dist/tools/http.js.map +1 -0
- package/dist/tools/index.d.ts +323 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +50 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/json.d.ts +28 -0
- package/dist/tools/json.d.ts.map +1 -0
- package/dist/tools/json.js +42 -0
- package/dist/tools/json.js.map +1 -0
- package/dist/tools/random.d.ts +29 -0
- package/dist/tools/random.d.ts.map +1 -0
- package/dist/tools/random.js +56 -0
- package/dist/tools/random.js.map +1 -0
- package/dist/tools/regex.d.ts +43 -0
- package/dist/tools/regex.d.ts.map +1 -0
- package/dist/tools/regex.js +68 -0
- package/dist/tools/regex.js.map +1 -0
- package/dist/tools/sleep.d.ts +10 -0
- package/dist/tools/sleep.d.ts.map +1 -0
- package/dist/tools/sleep.js +25 -0
- package/dist/tools/sleep.js.map +1 -0
- package/dist/tools/uuid.d.ts +15 -0
- package/dist/tools/uuid.d.ts.map +1 -0
- package/dist/tools/uuid.js +25 -0
- package/dist/tools/uuid.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +127 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +235 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/fallback.d.ts +66 -0
- package/dist/utils/fallback.d.ts.map +1 -0
- package/dist/utils/fallback.js +99 -0
- package/dist/utils/fallback.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/retry.d.ts +51 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +115 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../src/tools/sleep.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH,eAAO,MAAM,KAAK;;;;;EAWhB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sleep tool - pause execution for a specified duration
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { tool } from '../tool';
|
|
6
|
+
const sleepParams = z.object({
|
|
7
|
+
ms: z
|
|
8
|
+
.number()
|
|
9
|
+
.int()
|
|
10
|
+
.min(0)
|
|
11
|
+
.max(60000)
|
|
12
|
+
.describe('Duration to sleep in milliseconds (max: 60000 = 1 minute)'),
|
|
13
|
+
});
|
|
14
|
+
export const sleep = tool({
|
|
15
|
+
name: 'sleep',
|
|
16
|
+
description: 'Pause execution for a specified number of milliseconds. Useful for rate limiting or waiting between operations. Maximum: 60 seconds.',
|
|
17
|
+
parameters: sleepParams,
|
|
18
|
+
execute: async ({ ms }) => {
|
|
19
|
+
const start = Date.now();
|
|
20
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
21
|
+
const actual = Date.now() - start;
|
|
22
|
+
return { slept: actual, requested: ms };
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=sleep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep.js","sourceRoot":"","sources":["../../src/tools/sleep.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,EAAE,EAAE,CAAC;SACF,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,KAAK,CAAC;SACV,QAAQ,CAAC,2DAA2D,CAAC;CACzE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,OAAO;IACb,WAAW,EACT,sIAAsI;IACxI,UAAU,EAAE,WAAW;IACvB,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QAClC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC1C,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UUID tool - generates UUID v4
|
|
3
|
+
*/
|
|
4
|
+
export declare const uuid: import("@cogitator-ai/types").Tool<{
|
|
5
|
+
count?: number | undefined;
|
|
6
|
+
}, {
|
|
7
|
+
uuid: `${string}-${string}-${string}-${string}-${string}`;
|
|
8
|
+
uuids?: undefined;
|
|
9
|
+
count?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
uuids: `${string}-${string}-${string}-${string}-${string}`[];
|
|
12
|
+
count: number;
|
|
13
|
+
uuid?: undefined;
|
|
14
|
+
}>;
|
|
15
|
+
//# sourceMappingURL=uuid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../../src/tools/uuid.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgBH,eAAO,MAAM,IAAI;;;;;;;;;;EAQf,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UUID tool - generates UUID v4
|
|
3
|
+
*/
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { tool } from '../tool';
|
|
7
|
+
const uuidParams = z.object({
|
|
8
|
+
count: z
|
|
9
|
+
.number()
|
|
10
|
+
.int()
|
|
11
|
+
.min(1)
|
|
12
|
+
.max(100)
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Number of UUIDs to generate (default: 1, max: 100)'),
|
|
15
|
+
});
|
|
16
|
+
export const uuid = tool({
|
|
17
|
+
name: 'uuid',
|
|
18
|
+
description: 'Generate one or more UUID v4 identifiers.',
|
|
19
|
+
parameters: uuidParams,
|
|
20
|
+
execute: async ({ count = 1 }) => {
|
|
21
|
+
const uuids = Array.from({ length: count }, () => randomUUID());
|
|
22
|
+
return count === 1 ? { uuid: uuids[0] } : { uuids, count };
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=uuid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../../src/tools/uuid.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,oDAAoD,CAAC;CAClE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,2CAA2C;IACxD,UAAU,EAAE,UAAU;IACtB,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,GAAG,CAAC,EAAE,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;QAChE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC7D,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker pattern implementation
|
|
3
|
+
*
|
|
4
|
+
* Prevents cascading failures by failing fast when a service is unhealthy.
|
|
5
|
+
* States:
|
|
6
|
+
* - CLOSED: Normal operation, requests pass through
|
|
7
|
+
* - OPEN: Service is failing, requests fail immediately
|
|
8
|
+
* - HALF_OPEN: Testing if service recovered
|
|
9
|
+
*/
|
|
10
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
11
|
+
export interface CircuitBreakerOptions {
|
|
12
|
+
/** Number of failures before opening circuit (default: 5) */
|
|
13
|
+
failureThreshold?: number;
|
|
14
|
+
/** Time in ms before trying again after opening (default: 30000) */
|
|
15
|
+
resetTimeout?: number;
|
|
16
|
+
/** Max requests allowed in half-open state (default: 3) */
|
|
17
|
+
halfOpenRequests?: number;
|
|
18
|
+
/** Function to determine if error should count as failure */
|
|
19
|
+
isFailure?: (error: Error) => boolean;
|
|
20
|
+
/** Called when state changes */
|
|
21
|
+
onStateChange?: (from: CircuitState, to: CircuitState) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface CircuitBreakerStats {
|
|
24
|
+
state: CircuitState;
|
|
25
|
+
failures: number;
|
|
26
|
+
successes: number;
|
|
27
|
+
totalRequests: number;
|
|
28
|
+
lastFailure?: Date;
|
|
29
|
+
lastSuccess?: Date;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Circuit Breaker for protecting external service calls
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const breaker = new CircuitBreaker({
|
|
37
|
+
* failureThreshold: 5,
|
|
38
|
+
* resetTimeout: 30000,
|
|
39
|
+
* onStateChange: (from, to) => {
|
|
40
|
+
* console.log(`Circuit ${from} -> ${to}`);
|
|
41
|
+
* },
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* try {
|
|
45
|
+
* const result = await breaker.execute(() => callExternalService());
|
|
46
|
+
* } catch (error) {
|
|
47
|
+
* if (error.code === ErrorCode.CIRCUIT_OPEN) {
|
|
48
|
+
* // Circuit is open, service is unhealthy
|
|
49
|
+
* }
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare class CircuitBreaker {
|
|
54
|
+
private state;
|
|
55
|
+
private failures;
|
|
56
|
+
private successes;
|
|
57
|
+
private totalRequests;
|
|
58
|
+
private halfOpenRequests;
|
|
59
|
+
private lastFailure?;
|
|
60
|
+
private lastSuccess?;
|
|
61
|
+
private openedAt?;
|
|
62
|
+
private readonly options;
|
|
63
|
+
private readonly isFailure;
|
|
64
|
+
private readonly onStateChange?;
|
|
65
|
+
constructor(options?: CircuitBreakerOptions);
|
|
66
|
+
/**
|
|
67
|
+
* Get current circuit state
|
|
68
|
+
*/
|
|
69
|
+
getState(): CircuitState;
|
|
70
|
+
/**
|
|
71
|
+
* Get circuit statistics
|
|
72
|
+
*/
|
|
73
|
+
getStats(): CircuitBreakerStats;
|
|
74
|
+
/**
|
|
75
|
+
* Check if circuit should transition from open to half-open
|
|
76
|
+
*/
|
|
77
|
+
private shouldAttemptReset;
|
|
78
|
+
/**
|
|
79
|
+
* Transition to a new state
|
|
80
|
+
*/
|
|
81
|
+
private transitionTo;
|
|
82
|
+
/**
|
|
83
|
+
* Record a successful call
|
|
84
|
+
*/
|
|
85
|
+
private recordSuccess;
|
|
86
|
+
/**
|
|
87
|
+
* Record a failed call
|
|
88
|
+
*/
|
|
89
|
+
private recordFailure;
|
|
90
|
+
/**
|
|
91
|
+
* Execute a function through the circuit breaker
|
|
92
|
+
*/
|
|
93
|
+
execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
94
|
+
/**
|
|
95
|
+
* Force circuit to open state
|
|
96
|
+
*/
|
|
97
|
+
open(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Force circuit to closed state
|
|
100
|
+
*/
|
|
101
|
+
close(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Reset circuit to initial state
|
|
104
|
+
*/
|
|
105
|
+
reset(): void;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Registry for managing multiple circuit breakers
|
|
109
|
+
*/
|
|
110
|
+
export declare class CircuitBreakerRegistry {
|
|
111
|
+
private breakers;
|
|
112
|
+
private readonly defaultOptions;
|
|
113
|
+
constructor(defaultOptions?: CircuitBreakerOptions);
|
|
114
|
+
/**
|
|
115
|
+
* Get or create a circuit breaker by name
|
|
116
|
+
*/
|
|
117
|
+
get(name: string, options?: CircuitBreakerOptions): CircuitBreaker;
|
|
118
|
+
/**
|
|
119
|
+
* Get all circuit breaker states
|
|
120
|
+
*/
|
|
121
|
+
getAllStats(): Record<string, CircuitBreakerStats>;
|
|
122
|
+
/**
|
|
123
|
+
* Reset all circuit breakers
|
|
124
|
+
*/
|
|
125
|
+
resetAll(): void;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/utils/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACpC,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACtC,gCAAgC;IAChC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,CAAC;CAChE;AAQD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,WAAW,CAAC,CAAO;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAO;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuE;IAC/F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAiD;gBAEpE,OAAO,GAAE,qBAA0B;IAM/C;;OAEG;IACH,QAAQ,IAAI,YAAY;IAIxB;;OAEG;IACH,QAAQ,IAAI,mBAAmB;IAW/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAmBpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,aAAa;IAcrB;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAoClD;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,KAAK,IAAI,IAAI;CAUd;AAED;;GAEG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;gBAE3C,cAAc,GAAE,qBAA0B;IAItD;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,cAAc;IASlE;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAQlD;;OAEG;IACH,QAAQ,IAAI,IAAI;CAKjB"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker pattern implementation
|
|
3
|
+
*
|
|
4
|
+
* Prevents cascading failures by failing fast when a service is unhealthy.
|
|
5
|
+
* States:
|
|
6
|
+
* - CLOSED: Normal operation, requests pass through
|
|
7
|
+
* - OPEN: Service is failing, requests fail immediately
|
|
8
|
+
* - HALF_OPEN: Testing if service recovered
|
|
9
|
+
*/
|
|
10
|
+
import { CogitatorError, ErrorCode, isRetryableError } from '@cogitator-ai/types';
|
|
11
|
+
const DEFAULT_OPTIONS = {
|
|
12
|
+
failureThreshold: 5,
|
|
13
|
+
resetTimeout: 30000,
|
|
14
|
+
halfOpenRequests: 3,
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Circuit Breaker for protecting external service calls
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const breaker = new CircuitBreaker({
|
|
22
|
+
* failureThreshold: 5,
|
|
23
|
+
* resetTimeout: 30000,
|
|
24
|
+
* onStateChange: (from, to) => {
|
|
25
|
+
* console.log(`Circuit ${from} -> ${to}`);
|
|
26
|
+
* },
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* try {
|
|
30
|
+
* const result = await breaker.execute(() => callExternalService());
|
|
31
|
+
* } catch (error) {
|
|
32
|
+
* if (error.code === ErrorCode.CIRCUIT_OPEN) {
|
|
33
|
+
* // Circuit is open, service is unhealthy
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export class CircuitBreaker {
|
|
39
|
+
state = 'closed';
|
|
40
|
+
failures = 0;
|
|
41
|
+
successes = 0;
|
|
42
|
+
totalRequests = 0;
|
|
43
|
+
halfOpenRequests = 0;
|
|
44
|
+
lastFailure;
|
|
45
|
+
lastSuccess;
|
|
46
|
+
openedAt;
|
|
47
|
+
options;
|
|
48
|
+
isFailure;
|
|
49
|
+
onStateChange;
|
|
50
|
+
constructor(options = {}) {
|
|
51
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
52
|
+
this.isFailure = options.isFailure ?? isRetryableError;
|
|
53
|
+
this.onStateChange = options.onStateChange;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get current circuit state
|
|
57
|
+
*/
|
|
58
|
+
getState() {
|
|
59
|
+
return this.state;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get circuit statistics
|
|
63
|
+
*/
|
|
64
|
+
getStats() {
|
|
65
|
+
return {
|
|
66
|
+
state: this.state,
|
|
67
|
+
failures: this.failures,
|
|
68
|
+
successes: this.successes,
|
|
69
|
+
totalRequests: this.totalRequests,
|
|
70
|
+
lastFailure: this.lastFailure,
|
|
71
|
+
lastSuccess: this.lastSuccess,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if circuit should transition from open to half-open
|
|
76
|
+
*/
|
|
77
|
+
shouldAttemptReset() {
|
|
78
|
+
if (this.state !== 'open' || !this.openedAt) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const elapsed = Date.now() - this.openedAt.getTime();
|
|
82
|
+
return elapsed >= this.options.resetTimeout;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Transition to a new state
|
|
86
|
+
*/
|
|
87
|
+
transitionTo(newState) {
|
|
88
|
+
if (this.state === newState)
|
|
89
|
+
return;
|
|
90
|
+
const oldState = this.state;
|
|
91
|
+
this.state = newState;
|
|
92
|
+
if (newState === 'open') {
|
|
93
|
+
this.openedAt = new Date();
|
|
94
|
+
this.halfOpenRequests = 0;
|
|
95
|
+
}
|
|
96
|
+
else if (newState === 'closed') {
|
|
97
|
+
this.failures = 0;
|
|
98
|
+
this.halfOpenRequests = 0;
|
|
99
|
+
}
|
|
100
|
+
else if (newState === 'half-open') {
|
|
101
|
+
this.halfOpenRequests = 0;
|
|
102
|
+
}
|
|
103
|
+
this.onStateChange?.(oldState, newState);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Record a successful call
|
|
107
|
+
*/
|
|
108
|
+
recordSuccess() {
|
|
109
|
+
this.successes++;
|
|
110
|
+
this.lastSuccess = new Date();
|
|
111
|
+
if (this.state === 'half-open') {
|
|
112
|
+
this.halfOpenRequests++;
|
|
113
|
+
if (this.halfOpenRequests >= this.options.halfOpenRequests) {
|
|
114
|
+
this.transitionTo('closed');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (this.state === 'closed') {
|
|
118
|
+
this.failures = 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Record a failed call
|
|
123
|
+
*/
|
|
124
|
+
recordFailure() {
|
|
125
|
+
this.failures++;
|
|
126
|
+
this.lastFailure = new Date();
|
|
127
|
+
if (this.state === 'half-open') {
|
|
128
|
+
this.transitionTo('open');
|
|
129
|
+
}
|
|
130
|
+
else if (this.state === 'closed') {
|
|
131
|
+
if (this.failures >= this.options.failureThreshold) {
|
|
132
|
+
this.transitionTo('open');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Execute a function through the circuit breaker
|
|
138
|
+
*/
|
|
139
|
+
async execute(fn) {
|
|
140
|
+
this.totalRequests++;
|
|
141
|
+
if (this.shouldAttemptReset()) {
|
|
142
|
+
this.transitionTo('half-open');
|
|
143
|
+
}
|
|
144
|
+
if (this.state === 'open') {
|
|
145
|
+
throw new CogitatorError({
|
|
146
|
+
message: 'Circuit breaker is open',
|
|
147
|
+
code: ErrorCode.CIRCUIT_OPEN,
|
|
148
|
+
retryable: true,
|
|
149
|
+
retryAfter: this.options.resetTimeout,
|
|
150
|
+
details: {
|
|
151
|
+
state: this.state,
|
|
152
|
+
failures: this.failures,
|
|
153
|
+
lastFailure: this.lastFailure?.toISOString(),
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const result = await fn();
|
|
159
|
+
this.recordSuccess();
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
164
|
+
if (this.isFailure(err)) {
|
|
165
|
+
this.recordFailure();
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Force circuit to open state
|
|
172
|
+
*/
|
|
173
|
+
open() {
|
|
174
|
+
this.transitionTo('open');
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Force circuit to closed state
|
|
178
|
+
*/
|
|
179
|
+
close() {
|
|
180
|
+
this.transitionTo('closed');
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Reset circuit to initial state
|
|
184
|
+
*/
|
|
185
|
+
reset() {
|
|
186
|
+
this.state = 'closed';
|
|
187
|
+
this.failures = 0;
|
|
188
|
+
this.successes = 0;
|
|
189
|
+
this.totalRequests = 0;
|
|
190
|
+
this.halfOpenRequests = 0;
|
|
191
|
+
this.lastFailure = undefined;
|
|
192
|
+
this.lastSuccess = undefined;
|
|
193
|
+
this.openedAt = undefined;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Registry for managing multiple circuit breakers
|
|
198
|
+
*/
|
|
199
|
+
export class CircuitBreakerRegistry {
|
|
200
|
+
breakers = new Map();
|
|
201
|
+
defaultOptions;
|
|
202
|
+
constructor(defaultOptions = {}) {
|
|
203
|
+
this.defaultOptions = defaultOptions;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get or create a circuit breaker by name
|
|
207
|
+
*/
|
|
208
|
+
get(name, options) {
|
|
209
|
+
let breaker = this.breakers.get(name);
|
|
210
|
+
if (!breaker) {
|
|
211
|
+
breaker = new CircuitBreaker({ ...this.defaultOptions, ...options });
|
|
212
|
+
this.breakers.set(name, breaker);
|
|
213
|
+
}
|
|
214
|
+
return breaker;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Get all circuit breaker states
|
|
218
|
+
*/
|
|
219
|
+
getAllStats() {
|
|
220
|
+
const stats = {};
|
|
221
|
+
for (const [name, breaker] of this.breakers) {
|
|
222
|
+
stats[name] = breaker.getStats();
|
|
223
|
+
}
|
|
224
|
+
return stats;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Reset all circuit breakers
|
|
228
|
+
*/
|
|
229
|
+
resetAll() {
|
|
230
|
+
for (const breaker of this.breakers.values()) {
|
|
231
|
+
breaker.reset();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/utils/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback utilities for graceful degradation
|
|
3
|
+
*/
|
|
4
|
+
import { type CircuitBreakerRegistry } from './circuit-breaker';
|
|
5
|
+
import { type RetryOptions } from './retry';
|
|
6
|
+
/**
|
|
7
|
+
* Fallback chain configuration
|
|
8
|
+
*/
|
|
9
|
+
export interface FallbackConfig<T> {
|
|
10
|
+
/** Primary operation */
|
|
11
|
+
primary: () => Promise<T>;
|
|
12
|
+
/** Fallback operations in order of preference */
|
|
13
|
+
fallbacks: {
|
|
14
|
+
name: string;
|
|
15
|
+
fn: () => Promise<T>;
|
|
16
|
+
}[];
|
|
17
|
+
/** Retry options for each operation */
|
|
18
|
+
retry?: RetryOptions;
|
|
19
|
+
/** Circuit breaker registry for tracking failures */
|
|
20
|
+
circuitBreakers?: CircuitBreakerRegistry;
|
|
21
|
+
/** Called when falling back */
|
|
22
|
+
onFallback?: (from: string, to: string, error: Error) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Execute with fallback chain
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const result = await withFallback({
|
|
30
|
+
* primary: () => ollamaBackend.chat(request),
|
|
31
|
+
* fallbacks: [
|
|
32
|
+
* { name: 'openai', fn: () => openaiBackend.chat(request) },
|
|
33
|
+
* { name: 'anthropic', fn: () => anthropicBackend.chat(request) },
|
|
34
|
+
* ],
|
|
35
|
+
* retry: { maxRetries: 2 },
|
|
36
|
+
* onFallback: (from, to, error) => {
|
|
37
|
+
* console.warn(`Falling back from ${from} to ${to}: ${error.message}`);
|
|
38
|
+
* },
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function withFallback<T>(config: FallbackConfig<T>): Promise<T>;
|
|
43
|
+
/**
|
|
44
|
+
* LLM fallback chain configuration
|
|
45
|
+
*/
|
|
46
|
+
export interface LLMFallbackConfig {
|
|
47
|
+
providers: {
|
|
48
|
+
provider: string;
|
|
49
|
+
model: string;
|
|
50
|
+
}[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create an LLM request executor with automatic fallback
|
|
54
|
+
*/
|
|
55
|
+
export declare function createLLMFallbackExecutor(config: LLMFallbackConfig, circuitBreakers: CircuitBreakerRegistry): <T>(request: (provider: string, model: string) => Promise<T>, onFallback?: (from: string, to: string, error: Error) => void) => Promise<T>;
|
|
56
|
+
/**
|
|
57
|
+
* Graceful degradation wrapper
|
|
58
|
+
*
|
|
59
|
+
* Returns a default value if operation fails after all retries
|
|
60
|
+
*/
|
|
61
|
+
export declare function withGracefulDegradation<T>(fn: () => Promise<T>, options: {
|
|
62
|
+
defaultValue: T;
|
|
63
|
+
retry?: RetryOptions;
|
|
64
|
+
onDegraded?: (error: Error) => void;
|
|
65
|
+
}): Promise<T>;
|
|
66
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/utils/fallback.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,wBAAwB;IACxB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1B,iDAAiD;IACjD,SAAS,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;KACtB,EAAE,CAAC;IACJ,uCAAuC;IACvC,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,qDAAqD;IACrD,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,+BAA+B;IAC/B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA4C3E;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;CACL;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,iBAAiB,EACzB,eAAe,EAAE,sBAAsB,IAEM,CAAC,EAC5C,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACxD,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,KAC5D,OAAO,CAAC,CAAC,CAAC,CAcd;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,CAAC,EAC7C,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,EAAE;IACP,YAAY,EAAE,CAAC,CAAC;IAChB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACrC,GACA,OAAO,CAAC,CAAC,CAAC,CAWZ"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback utilities for graceful degradation
|
|
3
|
+
*/
|
|
4
|
+
import { CogitatorError, ErrorCode } from '@cogitator-ai/types';
|
|
5
|
+
import { withRetry } from './retry';
|
|
6
|
+
/**
|
|
7
|
+
* Execute with fallback chain
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const result = await withFallback({
|
|
12
|
+
* primary: () => ollamaBackend.chat(request),
|
|
13
|
+
* fallbacks: [
|
|
14
|
+
* { name: 'openai', fn: () => openaiBackend.chat(request) },
|
|
15
|
+
* { name: 'anthropic', fn: () => anthropicBackend.chat(request) },
|
|
16
|
+
* ],
|
|
17
|
+
* retry: { maxRetries: 2 },
|
|
18
|
+
* onFallback: (from, to, error) => {
|
|
19
|
+
* console.warn(`Falling back from ${from} to ${to}: ${error.message}`);
|
|
20
|
+
* },
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export async function withFallback(config) {
|
|
25
|
+
const { primary, fallbacks, retry, circuitBreakers, onFallback } = config;
|
|
26
|
+
const operations = [
|
|
27
|
+
{ name: 'primary', fn: primary },
|
|
28
|
+
...fallbacks,
|
|
29
|
+
];
|
|
30
|
+
let lastError;
|
|
31
|
+
for (let i = 0; i < operations.length; i++) {
|
|
32
|
+
const op = operations[i];
|
|
33
|
+
const breaker = circuitBreakers?.get(op.name);
|
|
34
|
+
try {
|
|
35
|
+
const execute = async () => {
|
|
36
|
+
if (breaker) {
|
|
37
|
+
return breaker.execute(op.fn);
|
|
38
|
+
}
|
|
39
|
+
return op.fn();
|
|
40
|
+
};
|
|
41
|
+
if (retry) {
|
|
42
|
+
return await withRetry(execute, retry);
|
|
43
|
+
}
|
|
44
|
+
return await execute();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
48
|
+
if (i < operations.length - 1) {
|
|
49
|
+
const nextOp = operations[i + 1];
|
|
50
|
+
onFallback?.(op.name, nextOp.name, lastError);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw new CogitatorError({
|
|
55
|
+
message: `All fallback options exhausted: ${lastError?.message}`,
|
|
56
|
+
code: ErrorCode.INTERNAL_ERROR,
|
|
57
|
+
cause: lastError,
|
|
58
|
+
details: {
|
|
59
|
+
triedOperations: operations.map((op) => op.name),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create an LLM request executor with automatic fallback
|
|
65
|
+
*/
|
|
66
|
+
export function createLLMFallbackExecutor(config, circuitBreakers) {
|
|
67
|
+
return async function executeLLMWithFallback(request, onFallback) {
|
|
68
|
+
const [primary, ...fallbacks] = config.providers;
|
|
69
|
+
return withFallback({
|
|
70
|
+
primary: () => request(primary.provider, primary.model),
|
|
71
|
+
fallbacks: fallbacks.map((fb) => ({
|
|
72
|
+
name: `${fb.provider}:${fb.model}`,
|
|
73
|
+
fn: () => request(fb.provider, fb.model),
|
|
74
|
+
})),
|
|
75
|
+
retry: { maxRetries: 2, baseDelay: 1000, backoff: 'exponential' },
|
|
76
|
+
circuitBreakers,
|
|
77
|
+
onFallback,
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Graceful degradation wrapper
|
|
83
|
+
*
|
|
84
|
+
* Returns a default value if operation fails after all retries
|
|
85
|
+
*/
|
|
86
|
+
export async function withGracefulDegradation(fn, options) {
|
|
87
|
+
try {
|
|
88
|
+
if (options.retry) {
|
|
89
|
+
return await withRetry(fn, options.retry);
|
|
90
|
+
}
|
|
91
|
+
return await fn();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
95
|
+
options.onDegraded?.(err);
|
|
96
|
+
return options.defaultValue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/utils/fallback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC"}
|