@trading-boy/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.
Files changed (79) hide show
  1. package/README.md +8 -0
  2. package/dist/.gitkeep +0 -0
  3. package/dist/config/env.d.ts +102 -0
  4. package/dist/config/env.d.ts.map +1 -0
  5. package/dist/config/env.js +178 -0
  6. package/dist/config/env.js.map +1 -0
  7. package/dist/config/index.d.ts +2 -0
  8. package/dist/config/index.d.ts.map +1 -0
  9. package/dist/config/index.js +2 -0
  10. package/dist/config/index.js.map +1 -0
  11. package/dist/constants/index.d.ts +49 -0
  12. package/dist/constants/index.d.ts.map +1 -0
  13. package/dist/constants/index.js +63 -0
  14. package/dist/constants/index.js.map +1 -0
  15. package/dist/crypto.d.ts +47 -0
  16. package/dist/crypto.d.ts.map +1 -0
  17. package/dist/crypto.js +56 -0
  18. package/dist/crypto.js.map +1 -0
  19. package/dist/errors/index.d.ts +25 -0
  20. package/dist/errors/index.d.ts.map +1 -0
  21. package/dist/errors/index.js +50 -0
  22. package/dist/errors/index.js.map +1 -0
  23. package/dist/index.d.ts +13 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +13 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/llm/client.d.ts +38 -0
  28. package/dist/llm/client.d.ts.map +1 -0
  29. package/dist/llm/client.js +194 -0
  30. package/dist/llm/client.js.map +1 -0
  31. package/dist/llm/config.d.ts +12 -0
  32. package/dist/llm/config.d.ts.map +1 -0
  33. package/dist/llm/config.js +47 -0
  34. package/dist/llm/config.js.map +1 -0
  35. package/dist/llm/index.d.ts +5 -0
  36. package/dist/llm/index.d.ts.map +1 -0
  37. package/dist/llm/index.js +4 -0
  38. package/dist/llm/index.js.map +1 -0
  39. package/dist/llm/structured-output.d.ts +31 -0
  40. package/dist/llm/structured-output.d.ts.map +1 -0
  41. package/dist/llm/structured-output.js +69 -0
  42. package/dist/llm/structured-output.js.map +1 -0
  43. package/dist/llm/types.d.ts +39 -0
  44. package/dist/llm/types.d.ts.map +1 -0
  45. package/dist/llm/types.js +6 -0
  46. package/dist/llm/types.js.map +1 -0
  47. package/dist/logging/index.d.ts +2 -0
  48. package/dist/logging/index.d.ts.map +1 -0
  49. package/dist/logging/index.js +2 -0
  50. package/dist/logging/index.js.map +1 -0
  51. package/dist/logging/logger.d.ts +7 -0
  52. package/dist/logging/logger.d.ts.map +1 -0
  53. package/dist/logging/logger.js +66 -0
  54. package/dist/logging/logger.js.map +1 -0
  55. package/dist/types/context-package.d.ts +100 -0
  56. package/dist/types/context-package.d.ts.map +1 -0
  57. package/dist/types/context-package.js +2 -0
  58. package/dist/types/context-package.js.map +1 -0
  59. package/dist/types/daily-summary.d.ts +212 -0
  60. package/dist/types/daily-summary.d.ts.map +1 -0
  61. package/dist/types/daily-summary.js +6 -0
  62. package/dist/types/daily-summary.js.map +1 -0
  63. package/dist/types/decision-traces.d.ts +123 -0
  64. package/dist/types/decision-traces.d.ts.map +1 -0
  65. package/dist/types/decision-traces.js +2 -0
  66. package/dist/types/decision-traces.js.map +1 -0
  67. package/dist/types/enums.d.ts +207 -0
  68. package/dist/types/enums.d.ts.map +1 -0
  69. package/dist/types/enums.js +247 -0
  70. package/dist/types/enums.js.map +1 -0
  71. package/dist/types/graph-edges.d.ts +127 -0
  72. package/dist/types/graph-edges.d.ts.map +1 -0
  73. package/dist/types/graph-edges.js +3 -0
  74. package/dist/types/graph-edges.js.map +1 -0
  75. package/dist/types/graph-nodes.d.ts +79 -0
  76. package/dist/types/graph-nodes.d.ts.map +1 -0
  77. package/dist/types/graph-nodes.js +2 -0
  78. package/dist/types/graph-nodes.js.map +1 -0
  79. package/package.json +31 -0
@@ -0,0 +1,13 @@
1
+ export * from './types/enums.js';
2
+ export * from './types/graph-nodes.js';
3
+ export * from './types/graph-edges.js';
4
+ export * from './types/context-package.js';
5
+ export * from './types/decision-traces.js';
6
+ export * from './types/daily-summary.js';
7
+ export * from './constants/index.js';
8
+ export * from './config/index.js';
9
+ export * from './logging/index.js';
10
+ export * from './errors/index.js';
11
+ export { computeDecisionHash, verifyChain, type ChainVerificationResult, type VerifiableDecision } from './crypto.js';
12
+ export * from './llm/index.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,uBAAuB,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtH,cAAc,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export * from './types/enums.js';
2
+ export * from './types/graph-nodes.js';
3
+ export * from './types/graph-edges.js';
4
+ export * from './types/context-package.js';
5
+ export * from './types/decision-traces.js';
6
+ export * from './types/daily-summary.js';
7
+ export * from './constants/index.js';
8
+ export * from './config/index.js';
9
+ export * from './logging/index.js';
10
+ export * from './errors/index.js';
11
+ export { computeDecisionHash, verifyChain } from './crypto.js';
12
+ export * from './llm/index.js';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAyD,MAAM,aAAa,CAAC;AACtH,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { TradingBoyError } from '../errors/index.js';
2
+ import type { LLMClientConfig, LLMCompletionOptions, LLMCompletionResult } from './types.js';
3
+ export declare class LLMError extends TradingBoyError {
4
+ constructor(message: string);
5
+ }
6
+ export declare class LLMDisabledError extends TradingBoyError {
7
+ constructor();
8
+ }
9
+ export declare class LLMClient {
10
+ private readonly primary;
11
+ private readonly fallback;
12
+ private readonly config;
13
+ constructor(config: LLMClientConfig);
14
+ /**
15
+ * Send a chat completion request with automatic retry + failover.
16
+ */
17
+ complete(options: LLMCompletionOptions): Promise<LLMCompletionResult>;
18
+ /**
19
+ * Health check — verify API connectivity.
20
+ */
21
+ healthCheck(): Promise<{
22
+ primary: boolean;
23
+ fallback: boolean;
24
+ }>;
25
+ private sendRequest;
26
+ private isRetryable;
27
+ }
28
+ /**
29
+ * Get the singleton LLM client. Reads config from environment.
30
+ * If ENABLE_LLM is false, returns a stub that throws LLMDisabledError.
31
+ */
32
+ export declare function getLLMClient(): LLMClient;
33
+ /**
34
+ * Reset the singleton. Used in tests.
35
+ * @internal
36
+ */
37
+ export declare function resetLLMClient(): void;
38
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAMpB,qBAAa,QAAS,SAAQ,eAAe;gBAC/B,OAAO,EAAE,MAAM;CAG5B;AAED,qBAAa,gBAAiB,SAAQ,eAAe;;CAOpD;AAID,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAE7B,MAAM,EAAE,eAAe;IAyBnC;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA6C3E;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;YAwBvD,WAAW;IA4CzB,OAAO,CAAC,WAAW;CAYpB;AAOD;;;GAGG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAqBxC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAGrC"}
@@ -0,0 +1,194 @@
1
+ // ─── LLM Client ───
2
+ //
3
+ // Model-agnostic LLM client with failover. Uses the `openai` npm package
4
+ // as a universal client — works with Anthropic, OpenRouter, Ollama, and
5
+ // any OpenAI-compatible endpoint via configurable baseURL.
6
+ import OpenAI from 'openai';
7
+ import { createLogger } from '../logging/index.js';
8
+ import { TradingBoyError } from '../errors/index.js';
9
+ import { resolveBaseURL, buildLLMConfig } from './config.js';
10
+ const logger = createLogger('llm');
11
+ // ─── Errors ───
12
+ export class LLMError extends TradingBoyError {
13
+ constructor(message) {
14
+ super(message, 'LLM_ERROR');
15
+ }
16
+ }
17
+ export class LLMDisabledError extends TradingBoyError {
18
+ constructor() {
19
+ super('LLM features are disabled. Set ENABLE_LLM=true to enable.', 'LLM_DISABLED');
20
+ }
21
+ }
22
+ // ─── Client ───
23
+ export class LLMClient {
24
+ primary;
25
+ fallback;
26
+ config;
27
+ constructor(config) {
28
+ this.config = config;
29
+ this.primary = new OpenAI({
30
+ apiKey: config.apiKey || 'ollama',
31
+ baseURL: resolveBaseURL(config.provider, config.baseURL),
32
+ timeout: config.timeoutMs ?? 30000,
33
+ maxRetries: 0, // We handle retries ourselves for failover control
34
+ defaultHeaders: config.provider === 'openrouter'
35
+ ? { 'HTTP-Referer': 'https://tradingboy.dev', 'X-Title': 'Trading Boy' }
36
+ : undefined,
37
+ });
38
+ if (config.fallback) {
39
+ this.fallback = new OpenAI({
40
+ apiKey: config.fallback.apiKey || config.apiKey || 'ollama',
41
+ baseURL: resolveBaseURL(config.fallback.provider, config.fallback.baseURL),
42
+ timeout: config.timeoutMs ?? 30000,
43
+ maxRetries: 0,
44
+ });
45
+ }
46
+ else {
47
+ this.fallback = null;
48
+ }
49
+ }
50
+ /**
51
+ * Send a chat completion request with automatic retry + failover.
52
+ */
53
+ async complete(options) {
54
+ const maxRetries = this.config.maxRetries ?? 2;
55
+ const model = options.model ?? this.config.model;
56
+ // Try primary with retries
57
+ let lastError = null;
58
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
59
+ try {
60
+ const result = await this.sendRequest(this.primary, model, this.config.provider, options);
61
+ return { ...result, fromFallback: false };
62
+ }
63
+ catch (error) {
64
+ lastError = error instanceof Error ? error : new Error(String(error));
65
+ if (attempt < maxRetries && this.isRetryable(error)) {
66
+ const delayMs = Math.pow(2, attempt) * 1000;
67
+ logger.warn({ attempt: attempt + 1, maxRetries, delayMs, error: lastError.message }, 'LLM request failed, retrying');
68
+ await sleep(delayMs);
69
+ }
70
+ }
71
+ }
72
+ // Try fallback if configured
73
+ if (this.fallback && this.config.fallback) {
74
+ logger.info('Primary LLM failed, attempting fallback');
75
+ try {
76
+ const result = await this.sendRequest(this.fallback, this.config.fallback.model, this.config.fallback.provider, options);
77
+ return { ...result, fromFallback: true };
78
+ }
79
+ catch (fallbackError) {
80
+ const msg = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
81
+ throw new LLMError(`Both primary and fallback LLM failed. Primary: ${lastError?.message}. Fallback: ${msg}`);
82
+ }
83
+ }
84
+ throw new LLMError(`LLM request failed after ${maxRetries + 1} attempts: ${lastError?.message}`);
85
+ }
86
+ /**
87
+ * Health check — verify API connectivity.
88
+ */
89
+ async healthCheck() {
90
+ const checkClient = async (client, model) => {
91
+ try {
92
+ await client.chat.completions.create({
93
+ model,
94
+ messages: [{ role: 'user', content: 'ping' }],
95
+ max_tokens: 1,
96
+ });
97
+ return true;
98
+ }
99
+ catch {
100
+ return false;
101
+ }
102
+ };
103
+ const primary = await checkClient(this.primary, this.config.model);
104
+ const fallback = this.fallback && this.config.fallback
105
+ ? await checkClient(this.fallback, this.config.fallback.model)
106
+ : false;
107
+ return { primary, fallback };
108
+ }
109
+ // ─── Private ───
110
+ async sendRequest(client, model, provider, options) {
111
+ const start = Date.now();
112
+ const response = await client.chat.completions.create({
113
+ model,
114
+ messages: options.messages,
115
+ temperature: options.temperature ?? 0,
116
+ max_tokens: options.maxTokens ?? 1024,
117
+ ...(options.jsonMode ? { response_format: { type: 'json_object' } } : {}),
118
+ });
119
+ const latencyMs = Date.now() - start;
120
+ const choice = response.choices[0];
121
+ const content = choice?.message?.content ?? '';
122
+ logger.debug({
123
+ model: response.model,
124
+ provider,
125
+ latencyMs,
126
+ inputTokens: response.usage?.prompt_tokens,
127
+ outputTokens: response.usage?.completion_tokens,
128
+ }, 'LLM request completed');
129
+ return {
130
+ content,
131
+ model: response.model,
132
+ provider,
133
+ usage: {
134
+ inputTokens: response.usage?.prompt_tokens ?? 0,
135
+ outputTokens: response.usage?.completion_tokens ?? 0,
136
+ totalTokens: response.usage?.total_tokens ?? 0,
137
+ },
138
+ latencyMs,
139
+ };
140
+ }
141
+ isRetryable(error) {
142
+ // Check for API errors with status codes (rate limit 429, server errors 5xx)
143
+ const status = error.status;
144
+ if (typeof status === 'number') {
145
+ return status === 429 || status >= 500;
146
+ }
147
+ // Retry on network errors
148
+ if (error instanceof Error && (error.message.includes('ECONNREFUSED') || error.message.includes('timeout'))) {
149
+ return true;
150
+ }
151
+ return false;
152
+ }
153
+ }
154
+ // ─── Singleton ───
155
+ let _client = null;
156
+ let _stubClient = null;
157
+ /**
158
+ * Get the singleton LLM client. Reads config from environment.
159
+ * If ENABLE_LLM is false, returns a stub that throws LLMDisabledError.
160
+ */
161
+ export function getLLMClient() {
162
+ const enableLLM = process.env.ENABLE_LLM === 'true';
163
+ if (!enableLLM) {
164
+ if (!_stubClient) {
165
+ _stubClient = new Proxy({}, {
166
+ get(_, prop) {
167
+ if (prop === 'complete' || prop === 'healthCheck') {
168
+ return () => { throw new LLMDisabledError(); };
169
+ }
170
+ return undefined;
171
+ },
172
+ });
173
+ }
174
+ return _stubClient;
175
+ }
176
+ if (!_client) {
177
+ const config = buildLLMConfig(process.env);
178
+ _client = new LLMClient(config);
179
+ }
180
+ return _client;
181
+ }
182
+ /**
183
+ * Reset the singleton. Used in tests.
184
+ * @internal
185
+ */
186
+ export function resetLLMClient() {
187
+ _client = null;
188
+ _stubClient = null;
189
+ }
190
+ // ─── Helpers ───
191
+ function sleep(ms) {
192
+ return new Promise((resolve) => setTimeout(resolve, ms));
193
+ }
194
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { LLMClientConfig } from './types.js';
2
+ /**
3
+ * Resolve the base URL for a provider.
4
+ * User-provided override takes precedence over preset.
5
+ */
6
+ export declare function resolveBaseURL(provider: string, override?: string): string;
7
+ /**
8
+ * Build LLMClientConfig from environment variables.
9
+ * Called by getLLMClient() singleton.
10
+ */
11
+ export declare function buildLLMConfig(env: Record<string, string | undefined>): LLMClientConfig;
12
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/llm/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,YAAY,CAAC;AAU/D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAG1E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,eAAe,CA0BvF"}
@@ -0,0 +1,47 @@
1
+ // ─── LLM Config ───
2
+ //
3
+ // Resolves provider-specific base URLs and builds LLMClientConfig from env.
4
+ // ─── Provider Base URLs ───
5
+ const PROVIDER_BASE_URLS = {
6
+ anthropic: 'https://api.anthropic.com/v1/',
7
+ openrouter: 'https://openrouter.ai/api/v1',
8
+ ollama: 'http://localhost:11434/v1',
9
+ };
10
+ /**
11
+ * Resolve the base URL for a provider.
12
+ * User-provided override takes precedence over preset.
13
+ */
14
+ export function resolveBaseURL(provider, override) {
15
+ if (override)
16
+ return override;
17
+ return PROVIDER_BASE_URLS[provider] ?? '';
18
+ }
19
+ /**
20
+ * Build LLMClientConfig from environment variables.
21
+ * Called by getLLMClient() singleton.
22
+ */
23
+ export function buildLLMConfig(env) {
24
+ const provider = (env.LLM_PROVIDER ?? 'anthropic');
25
+ const apiKey = env.LLM_API_KEY ?? env.ANTHROPIC_API_KEY ?? '';
26
+ const model = env.LLM_MODEL ?? 'claude-sonnet-4-6';
27
+ const config = {
28
+ provider,
29
+ apiKey,
30
+ baseURL: env.LLM_BASE_URL || undefined,
31
+ model,
32
+ maxRetries: env.LLM_MAX_RETRIES ? parseInt(env.LLM_MAX_RETRIES, 10) : 2,
33
+ timeoutMs: env.LLM_TIMEOUT_MS ? parseInt(env.LLM_TIMEOUT_MS, 10) : 30000,
34
+ };
35
+ // Configure fallback if specified
36
+ const fallbackProvider = env.LLM_FALLBACK_PROVIDER;
37
+ const fallbackModel = env.LLM_FALLBACK_MODEL;
38
+ if (fallbackProvider && fallbackModel) {
39
+ config.fallback = {
40
+ provider: fallbackProvider,
41
+ apiKey: env.LLM_FALLBACK_API_KEY,
42
+ model: fallbackModel,
43
+ };
44
+ }
45
+ return config;
46
+ }
47
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/llm/config.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,EAAE;AACF,4EAA4E;AAI5E,6BAA6B;AAE7B,MAAM,kBAAkB,GAA2B;IACjD,SAAS,EAAE,+BAA+B;IAC1C,UAAU,EAAE,8BAA8B;IAC1C,MAAM,EAAE,2BAA2B;CACpC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,QAAiB;IAChE,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,OAAO,kBAAkB,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAuC;IACpE,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,WAAW,CAAgB,CAAC;IAClE,MAAM,MAAM,GAAG,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC;IAC9D,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,IAAI,mBAAmB,CAAC;IAEnD,MAAM,MAAM,GAAoB;QAC9B,QAAQ;QACR,MAAM;QACN,OAAO,EAAE,GAAG,CAAC,YAAY,IAAI,SAAS;QACtC,KAAK;QACL,UAAU,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACvE,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK;KACzE,CAAC;IAEF,kCAAkC;IAClC,MAAM,gBAAgB,GAAG,GAAG,CAAC,qBAAqB,CAAC;IACnD,MAAM,aAAa,GAAG,GAAG,CAAC,kBAAkB,CAAC;IAC7C,IAAI,gBAAgB,IAAI,aAAa,EAAE,CAAC;QACtC,MAAM,CAAC,QAAQ,GAAG;YAChB,QAAQ,EAAE,gBAA+B;YACzC,MAAM,EAAE,GAAG,CAAC,oBAAoB;YAChC,KAAK,EAAE,aAAa;SACrB,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type { LLMProvider, LLMClientConfig, LLMMessage, LLMCompletionOptions, LLMCompletionResult, } from './types.js';
2
+ export { LLMClient, LLMError, LLMDisabledError, getLLMClient, resetLLMClient, } from './client.js';
3
+ export { extractStructured, StructuredOutputError, type ExtractOptions, type ExtractResult, } from './structured-output.js';
4
+ export { resolveBaseURL, buildLLMConfig } from './config.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,WAAW,EACX,eAAe,EACf,UAAU,EACV,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { LLMClient, LLMError, LLMDisabledError, getLLMClient, resetLLMClient, } from './client.js';
2
+ export { extractStructured, StructuredOutputError, } from './structured-output.js';
3
+ export { resolveBaseURL, buildLLMConfig } from './config.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GAGtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { z } from 'zod';
2
+ import { TradingBoyError } from '../errors/index.js';
3
+ import type { LLMClient } from './client.js';
4
+ import type { LLMCompletionResult, LLMMessage } from './types.js';
5
+ export declare class StructuredOutputError extends TradingBoyError {
6
+ constructor(message: string);
7
+ }
8
+ export interface ExtractOptions {
9
+ messages: LLMMessage[];
10
+ schemaDescription?: string;
11
+ temperature?: number;
12
+ maxTokens?: number;
13
+ maxRetries?: number;
14
+ }
15
+ export interface ExtractResult<T> {
16
+ data: T;
17
+ raw: string;
18
+ meta: LLMCompletionResult;
19
+ retried: boolean;
20
+ }
21
+ /**
22
+ * Extract structured data from an LLM response using Zod validation.
23
+ *
24
+ * 1. Appends schema description to messages (if provided)
25
+ * 2. Sends request with jsonMode=true
26
+ * 3. Parses response with schema
27
+ * 4. If validation fails, retries once with error feedback
28
+ * 5. If second attempt fails, throws StructuredOutputError
29
+ */
30
+ export declare function extractStructured<T>(client: LLMClient, schema: z.ZodSchema<T>, options: ExtractOptions): Promise<ExtractResult<T>>;
31
+ //# sourceMappingURL=structured-output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-output.d.ts","sourceRoot":"","sources":["../../src/llm/structured-output.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMlE,qBAAa,qBAAsB,SAAQ,eAAe;gBAC5C,OAAO,EAAE,MAAM;CAG5B;AAID,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,IAAI,EAAE,CAAC,CAAC;IACR,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAID;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EACtB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAwD3B"}
@@ -0,0 +1,69 @@
1
+ // ─── Structured Output Helper ───
2
+ //
3
+ // Zod-validated JSON extraction from LLM responses. Sends a completion
4
+ // request with jsonMode, parses the response with the provided schema,
5
+ // and retries once with error feedback if validation fails.
6
+ import { createLogger } from '../logging/index.js';
7
+ import { TradingBoyError } from '../errors/index.js';
8
+ const logger = createLogger('llm-structured');
9
+ // ─── Errors ───
10
+ export class StructuredOutputError extends TradingBoyError {
11
+ constructor(message) {
12
+ super(message, 'STRUCTURED_OUTPUT_ERROR');
13
+ }
14
+ }
15
+ // ─── Extract ───
16
+ /**
17
+ * Extract structured data from an LLM response using Zod validation.
18
+ *
19
+ * 1. Appends schema description to messages (if provided)
20
+ * 2. Sends request with jsonMode=true
21
+ * 3. Parses response with schema
22
+ * 4. If validation fails, retries once with error feedback
23
+ * 5. If second attempt fails, throws StructuredOutputError
24
+ */
25
+ export async function extractStructured(client, schema, options) {
26
+ const maxRetries = options.maxRetries ?? 1;
27
+ const messages = [...options.messages];
28
+ // Add schema description hint to system message if provided
29
+ if (options.schemaDescription) {
30
+ messages.push({
31
+ role: 'user',
32
+ content: `Respond with a JSON object matching this schema: ${options.schemaDescription}. Output ONLY valid JSON, no markdown or explanation.`,
33
+ });
34
+ }
35
+ let lastRaw = '';
36
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
37
+ const result = await client.complete({
38
+ messages,
39
+ temperature: options.temperature ?? 0,
40
+ maxTokens: options.maxTokens ?? 1024,
41
+ jsonMode: true,
42
+ });
43
+ lastRaw = result.content;
44
+ try {
45
+ const parsed = JSON.parse(result.content);
46
+ const validated = schema.parse(parsed);
47
+ return {
48
+ data: validated,
49
+ raw: result.content,
50
+ meta: result,
51
+ retried: attempt > 0,
52
+ };
53
+ }
54
+ catch (parseError) {
55
+ const errorMsg = parseError instanceof Error ? parseError.message : String(parseError);
56
+ if (attempt < maxRetries) {
57
+ logger.warn({ attempt: attempt + 1, error: errorMsg }, 'Structured output validation failed, retrying with error feedback');
58
+ // Add error feedback for retry
59
+ messages.push({ role: 'assistant', content: result.content });
60
+ messages.push({
61
+ role: 'user',
62
+ content: `Your JSON response was invalid: ${errorMsg}. Fix the errors and respond with valid JSON only.`,
63
+ });
64
+ }
65
+ }
66
+ }
67
+ throw new StructuredOutputError(`Failed to extract valid structured output after ${maxRetries + 1} attempts. Last response: ${lastRaw.slice(0, 200)}`);
68
+ }
69
+ //# sourceMappingURL=structured-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-output.js","sourceRoot":"","sources":["../../src/llm/structured-output.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,EAAE;AACF,uEAAuE;AACvE,uEAAuE;AACvE,4DAA4D;AAG5D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAIrD,MAAM,MAAM,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;AAE9C,iBAAiB;AAEjB,MAAM,OAAO,qBAAsB,SAAQ,eAAe;IACxD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,yBAAyB,CAAC,CAAC;IAC5C,CAAC;CACF;AAmBD,kBAAkB;AAElB;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAiB,EACjB,MAAsB,EACtB,OAAuB;IAEvB,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEvC,4DAA4D;IAC5D,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,oDAAoD,OAAO,CAAC,iBAAiB,uDAAuD;SAC9I,CAAC,CAAC;IACL,CAAC;IAED,IAAI,OAAO,GAAG,EAAE,CAAC;IAEjB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC;YACnC,QAAQ;YACR,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC;YACrC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;YACpC,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QAEH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAEzB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAEvC,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,GAAG,EAAE,MAAM,CAAC,OAAO;gBACnB,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,OAAO,GAAG,CAAC;aACrB,CAAC;QACJ,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAEvF,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,MAAM,CAAC,IAAI,CACT,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EACzC,mEAAmE,CACpE,CAAC;gBAEF,+BAA+B;gBAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC9D,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,mCAAmC,QAAQ,oDAAoD;iBACzG,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,qBAAqB,CAC7B,mDAAmD,UAAU,GAAG,CAAC,6BAA6B,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACtH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,39 @@
1
+ export type LLMProvider = 'anthropic' | 'openrouter' | 'ollama' | 'custom';
2
+ export interface LLMClientConfig {
3
+ provider: LLMProvider;
4
+ apiKey: string;
5
+ baseURL?: string;
6
+ model: string;
7
+ maxRetries?: number;
8
+ timeoutMs?: number;
9
+ fallback?: {
10
+ provider: LLMProvider;
11
+ apiKey?: string;
12
+ baseURL?: string;
13
+ model: string;
14
+ };
15
+ }
16
+ export interface LLMMessage {
17
+ role: 'system' | 'user' | 'assistant';
18
+ content: string;
19
+ }
20
+ export interface LLMCompletionOptions {
21
+ messages: LLMMessage[];
22
+ model?: string;
23
+ temperature?: number;
24
+ maxTokens?: number;
25
+ jsonMode?: boolean;
26
+ }
27
+ export interface LLMCompletionResult {
28
+ content: string;
29
+ model: string;
30
+ provider: string;
31
+ usage: {
32
+ inputTokens: number;
33
+ outputTokens: number;
34
+ totalTokens: number;
35
+ };
36
+ latencyMs: number;
37
+ fromFallback: boolean;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE;QACT,QAAQ,EAAE,WAAW,CAAC;QACtB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB"}
@@ -0,0 +1,6 @@
1
+ // ─── LLM Types ───
2
+ //
3
+ // Shared types for the model-agnostic LLM client.
4
+ // Uses OpenAI-compatible message format (works with Anthropic, OpenRouter, Ollama).
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,EAAE;AACF,kDAAkD;AAClD,oFAAoF"}
@@ -0,0 +1,2 @@
1
+ export { logger, createLogger, REDACT_PATHS, type Logger } from './logger.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { logger, createLogger, REDACT_PATHS } from './logger.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/logging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type Logger } from 'pino';
2
+ export declare const REDACT_PATHS: string[];
3
+ declare const baseLogger: Logger;
4
+ export declare function createLogger(name: string): Logger;
5
+ export { baseLogger as logger };
6
+ export type { Logger };
7
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAmDzC,eAAO,MAAM,YAAY,UAAuD,CAAC;AAEjF,QAAA,MAAM,UAAU,EAAE,MAahB,CAAC;AAEH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,OAAO,EAAE,UAAU,IAAI,MAAM,EAAE,CAAC;AAChC,YAAY,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,66 @@
1
+ import pino from 'pino';
2
+ const isDev = process.env.NODE_ENV !== 'production';
3
+ /**
4
+ * Sensitive field names that must be redacted from all log output.
5
+ * Auth-related `token` is intentionally included; crypto token fields
6
+ * use different names (`tokenSymbol`, `tokenAddress`, etc.) and are
7
+ * not affected.
8
+ */
9
+ const SENSITIVE_FIELDS = [
10
+ 'password',
11
+ 'apiKey',
12
+ 'api_key',
13
+ 'authorization',
14
+ 'secret',
15
+ 'token',
16
+ 'privateKey',
17
+ 'email',
18
+ 'customer_email',
19
+ 'x-api-key',
20
+ 'stripe_signature',
21
+ ];
22
+ /** Maximum object nesting depth to apply redaction. */
23
+ const MAX_REDACT_DEPTH = 5;
24
+ /**
25
+ * Build redaction paths for a field name at every nesting depth from 0
26
+ * up to MAX_REDACT_DEPTH.
27
+ *
28
+ * Pino uses `@pinojs/redact` which supports single `*` wildcards (one
29
+ * level) but not `**` recursive globs. We therefore generate explicit
30
+ * paths for each depth:
31
+ * depth 0 → `password`
32
+ * depth 1 → `*.password`
33
+ * depth 2 → `*.*.password`
34
+ * ...
35
+ */
36
+ function buildRedactPaths(fields, maxDepth) {
37
+ const paths = [];
38
+ for (const field of fields) {
39
+ for (let depth = 0; depth <= maxDepth; depth++) {
40
+ const prefix = '*.'
41
+ .repeat(depth);
42
+ paths.push(`${prefix}${field}`);
43
+ }
44
+ }
45
+ return paths;
46
+ }
47
+ export const REDACT_PATHS = buildRedactPaths(SENSITIVE_FIELDS, MAX_REDACT_DEPTH);
48
+ const baseLogger = pino({
49
+ name: 'trading-boy',
50
+ level: process.env.LOG_LEVEL ?? 'info',
51
+ redact: {
52
+ paths: REDACT_PATHS,
53
+ censor: '[REDACTED]',
54
+ },
55
+ ...(isDev && {
56
+ transport: {
57
+ target: 'pino-pretty',
58
+ options: { destination: 2 },
59
+ },
60
+ }),
61
+ });
62
+ export function createLogger(name) {
63
+ return baseLogger.child({ component: name });
64
+ }
65
+ export { baseLogger as logger };
66
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAqB,MAAM,MAAM,CAAC;AAEzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG;IACvB,UAAU;IACV,QAAQ;IACR,SAAS;IACT,eAAe;IACf,QAAQ;IACR,OAAO;IACP,YAAY;IACZ,OAAO;IACP,gBAAgB;IAChB,WAAW;IACX,kBAAkB;CACV,CAAC;AAEX,uDAAuD;AACvD,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B;;;;;;;;;;;GAWG;AACH,SAAS,gBAAgB,CAAC,MAAyB,EAAE,QAAgB;IACnE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAG,IAAI;iBAChB,MAAM,CAAC,KAAK,CAAC,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;AAEjF,MAAM,UAAU,GAAW,IAAI,CAAC;IAC9B,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;IACtC,MAAM,EAAE;QACN,KAAK,EAAE,YAAY;QACnB,MAAM,EAAE,YAAY;KACrB;IACD,GAAG,CAAC,KAAK,IAAI;QACX,SAAS,EAAE;YACT,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE;SAC5B;KACF,CAAC;CACH,CAAC,CAAC;AAEH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,UAAU,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,OAAO,EAAE,UAAU,IAAI,MAAM,EAAE,CAAC"}