@looopy-ai/core 1.0.1

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 (132) hide show
  1. package/LICENSE +9 -0
  2. package/dist/core/agent.d.ts +53 -0
  3. package/dist/core/agent.js +416 -0
  4. package/dist/core/cleanup.d.ts +12 -0
  5. package/dist/core/cleanup.js +45 -0
  6. package/dist/core/index.d.ts +3 -0
  7. package/dist/core/index.js +3 -0
  8. package/dist/core/iteration.d.ts +5 -0
  9. package/dist/core/iteration.js +60 -0
  10. package/dist/core/logger.d.ts +11 -0
  11. package/dist/core/logger.js +31 -0
  12. package/dist/core/loop.d.ts +5 -0
  13. package/dist/core/loop.js +125 -0
  14. package/dist/core/tools.d.ts +4 -0
  15. package/dist/core/tools.js +78 -0
  16. package/dist/core/types.d.ts +30 -0
  17. package/dist/core/types.js +1 -0
  18. package/dist/events/index.d.ts +3 -0
  19. package/dist/events/index.js +2 -0
  20. package/dist/events/utils.d.ts +250 -0
  21. package/dist/events/utils.js +263 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.js +8 -0
  24. package/dist/observability/index.d.ts +1 -0
  25. package/dist/observability/index.js +1 -0
  26. package/dist/observability/spans/agent-turn.d.ts +31 -0
  27. package/dist/observability/spans/agent-turn.js +94 -0
  28. package/dist/observability/spans/index.d.ts +5 -0
  29. package/dist/observability/spans/index.js +5 -0
  30. package/dist/observability/spans/iteration.d.ts +14 -0
  31. package/dist/observability/spans/iteration.js +41 -0
  32. package/dist/observability/spans/llm-call.d.ts +14 -0
  33. package/dist/observability/spans/llm-call.js +50 -0
  34. package/dist/observability/spans/loop.d.ts +14 -0
  35. package/dist/observability/spans/loop.js +40 -0
  36. package/dist/observability/spans/tool.d.ts +14 -0
  37. package/dist/observability/spans/tool.js +44 -0
  38. package/dist/observability/tracing.d.ts +58 -0
  39. package/dist/observability/tracing.js +203 -0
  40. package/dist/providers/chat-completions/aggregate.d.ts +4 -0
  41. package/dist/providers/chat-completions/aggregate.js +152 -0
  42. package/dist/providers/chat-completions/content.d.ts +25 -0
  43. package/dist/providers/chat-completions/content.js +229 -0
  44. package/dist/providers/chat-completions/index.d.ts +4 -0
  45. package/dist/providers/chat-completions/index.js +4 -0
  46. package/dist/providers/chat-completions/streaming.d.ts +12 -0
  47. package/dist/providers/chat-completions/streaming.js +3 -0
  48. package/dist/providers/chat-completions/types.d.ts +39 -0
  49. package/dist/providers/chat-completions/types.js +1 -0
  50. package/dist/providers/index.d.ts +2 -0
  51. package/dist/providers/index.js +1 -0
  52. package/dist/providers/litellm-provider.d.ts +43 -0
  53. package/dist/providers/litellm-provider.js +377 -0
  54. package/dist/server/event-buffer.d.ts +37 -0
  55. package/dist/server/event-buffer.js +116 -0
  56. package/dist/server/event-router.d.ts +31 -0
  57. package/dist/server/event-router.js +91 -0
  58. package/dist/server/index.d.ts +3 -0
  59. package/dist/server/index.js +3 -0
  60. package/dist/server/sse.d.ts +60 -0
  61. package/dist/server/sse.js +159 -0
  62. package/dist/stores/artifacts/artifact-scheduler.d.ts +50 -0
  63. package/dist/stores/artifacts/artifact-scheduler.js +86 -0
  64. package/dist/stores/artifacts/index.d.ts +3 -0
  65. package/dist/stores/artifacts/index.js +3 -0
  66. package/dist/stores/artifacts/internal-event-artifact-store.d.ts +54 -0
  67. package/dist/stores/artifacts/internal-event-artifact-store.js +126 -0
  68. package/dist/stores/artifacts/memory-artifact-store.d.ts +52 -0
  69. package/dist/stores/artifacts/memory-artifact-store.js +268 -0
  70. package/dist/stores/filesystem/filesystem-agent-store.d.ts +18 -0
  71. package/dist/stores/filesystem/filesystem-agent-store.js +61 -0
  72. package/dist/stores/filesystem/filesystem-artifact-store.d.ts +59 -0
  73. package/dist/stores/filesystem/filesystem-artifact-store.js +325 -0
  74. package/dist/stores/filesystem/filesystem-context-store.d.ts +37 -0
  75. package/dist/stores/filesystem/filesystem-context-store.js +245 -0
  76. package/dist/stores/filesystem/filesystem-message-store.d.ts +28 -0
  77. package/dist/stores/filesystem/filesystem-message-store.js +149 -0
  78. package/dist/stores/filesystem/filesystem-task-state-store.d.ts +27 -0
  79. package/dist/stores/filesystem/filesystem-task-state-store.js +220 -0
  80. package/dist/stores/filesystem/index.d.ts +10 -0
  81. package/dist/stores/filesystem/index.js +5 -0
  82. package/dist/stores/index.d.ts +5 -0
  83. package/dist/stores/index.js +5 -0
  84. package/dist/stores/memory/memory-state-store.d.ts +15 -0
  85. package/dist/stores/memory/memory-state-store.js +55 -0
  86. package/dist/stores/messages/hybrid-message-store.d.ts +29 -0
  87. package/dist/stores/messages/hybrid-message-store.js +72 -0
  88. package/dist/stores/messages/index.d.ts +4 -0
  89. package/dist/stores/messages/index.js +4 -0
  90. package/dist/stores/messages/interfaces.d.ts +42 -0
  91. package/dist/stores/messages/interfaces.js +18 -0
  92. package/dist/stores/messages/mem0-message-store.d.ts +34 -0
  93. package/dist/stores/messages/mem0-message-store.js +218 -0
  94. package/dist/stores/messages/memory-message-store.d.ts +27 -0
  95. package/dist/stores/messages/memory-message-store.js +183 -0
  96. package/dist/tools/artifact-tools.d.ts +4 -0
  97. package/dist/tools/artifact-tools.js +277 -0
  98. package/dist/tools/client-tool-provider.d.ts +25 -0
  99. package/dist/tools/client-tool-provider.js +139 -0
  100. package/dist/tools/index.d.ts +4 -0
  101. package/dist/tools/index.js +4 -0
  102. package/dist/tools/local-tools.d.ts +13 -0
  103. package/dist/tools/local-tools.js +70 -0
  104. package/dist/tools/mcp-client.d.ts +29 -0
  105. package/dist/tools/mcp-client.js +62 -0
  106. package/dist/tools/mcp-tool-provider.d.ts +22 -0
  107. package/dist/tools/mcp-tool-provider.js +86 -0
  108. package/dist/types/a2a.d.ts +36 -0
  109. package/dist/types/a2a.js +1 -0
  110. package/dist/types/agent.d.ts +14 -0
  111. package/dist/types/agent.js +1 -0
  112. package/dist/types/artifact.d.ts +126 -0
  113. package/dist/types/artifact.js +1 -0
  114. package/dist/types/context.d.ts +13 -0
  115. package/dist/types/context.js +1 -0
  116. package/dist/types/event.d.ts +360 -0
  117. package/dist/types/event.js +30 -0
  118. package/dist/types/index.d.ts +9 -0
  119. package/dist/types/index.js +9 -0
  120. package/dist/types/llm.d.ts +24 -0
  121. package/dist/types/llm.js +1 -0
  122. package/dist/types/message.d.ts +9 -0
  123. package/dist/types/message.js +1 -0
  124. package/dist/types/state.d.ts +86 -0
  125. package/dist/types/state.js +1 -0
  126. package/dist/types/tools.d.ts +57 -0
  127. package/dist/types/tools.js +53 -0
  128. package/dist/utils/error.d.ts +8 -0
  129. package/dist/utils/error.js +23 -0
  130. package/dist/utils/process-signals.d.ts +3 -0
  131. package/dist/utils/process-signals.js +67 -0
  132. package/package.json +54 -0
@@ -0,0 +1,12 @@
1
+ import type { Choice, LLMUsage } from './types';
2
+ type ChatCompletionStreamData = {
3
+ id: string;
4
+ created: number;
5
+ model: string;
6
+ object: string;
7
+ choices: Choice[];
8
+ usage?: LLMUsage;
9
+ };
10
+ export declare const choices: <T extends ChatCompletionStreamData>() => import("rxjs").UnaryFunction<import("rxjs").Observable<T>, import("rxjs").Observable<Choice>>;
11
+ export declare const usage: <T extends ChatCompletionStreamData>() => import("rxjs").UnaryFunction<import("rxjs").Observable<T>, import("rxjs").Observable<LLMUsage>>;
12
+ export {};
@@ -0,0 +1,3 @@
1
+ import { filter, map, mergeMap, pipe } from 'rxjs';
2
+ export const choices = () => pipe(mergeMap((data) => data.choices), filter((choice) => !!choice));
3
+ export const usage = () => pipe(map((data) => data.usage), filter((usage) => !!usage));
@@ -0,0 +1,39 @@
1
+ export type ToolCall = {
2
+ index: number;
3
+ id?: string | null;
4
+ function?: {
5
+ name?: string;
6
+ arguments?: string;
7
+ };
8
+ type?: 'function';
9
+ };
10
+ export type InlineXml = {
11
+ name: string;
12
+ content?: string;
13
+ attributes: Record<string, string | string[]>;
14
+ };
15
+ export type Choice = {
16
+ delta?: {
17
+ content?: string;
18
+ tool_calls?: ToolCall[];
19
+ };
20
+ index: number;
21
+ finish_reason?: string | null;
22
+ thoughts?: InlineXml[];
23
+ };
24
+ export type ChatCompletionStreamData = {
25
+ id: string;
26
+ created: string;
27
+ model: string;
28
+ object: string;
29
+ choices: Choice[];
30
+ };
31
+ export type LLMUsage = {
32
+ prompt_tokens?: number;
33
+ completion_tokens?: number;
34
+ total_tokens?: number;
35
+ completion_tokens_details?: Record<string, number>;
36
+ prompt_tokens_details?: Record<string, number>;
37
+ cache_creation_input_tokens?: number;
38
+ cache_read_input_tokens?: number;
39
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export type { LiteLLMConfig } from './litellm-provider';
2
+ export { LiteLLM, LiteLLMProvider } from './litellm-provider';
@@ -0,0 +1 @@
1
+ export { LiteLLM, LiteLLMProvider } from './litellm-provider';
@@ -0,0 +1,43 @@
1
+ import { Observable } from 'rxjs';
2
+ import type { AnyEvent, LLMEvent } from '../types/event';
3
+ import type { LLMProvider } from '../types/llm';
4
+ import type { Message } from '../types/message';
5
+ import type { ToolDefinition } from '../types/tools';
6
+ export interface LiteLLMConfig {
7
+ baseUrl: string;
8
+ model: string;
9
+ apiKey?: string;
10
+ temperature?: number;
11
+ maxTokens?: number;
12
+ topP?: number;
13
+ timeout?: number;
14
+ extraParams?: Record<string, unknown>;
15
+ debugLogPath?: string;
16
+ }
17
+ export declare class LiteLLMProvider implements LLMProvider {
18
+ private readonly config;
19
+ private debugLogInitialized;
20
+ private readonly logger;
21
+ constructor(config: LiteLLMConfig);
22
+ call(request: {
23
+ messages: Message[];
24
+ tools?: ToolDefinition[];
25
+ sessionId?: string;
26
+ }): Observable<LLMEvent<AnyEvent>>;
27
+ private debugLogRawChunk;
28
+ private debugLog;
29
+ private createSSEStream;
30
+ getModel(): string;
31
+ updateConfig(updates: Partial<LiteLLMConfig>): void;
32
+ }
33
+ export declare const LiteLLM: {
34
+ novaMicro(baseUrl: string, apiKey?: string): LiteLLMProvider;
35
+ novaLite(baseUrl: string, apiKey?: string, debugLogPath?: string): LiteLLMProvider;
36
+ gpt4(baseUrl: string, apiKey?: string): LiteLLMProvider;
37
+ gpt4Turbo(baseUrl: string, apiKey?: string): LiteLLMProvider;
38
+ gpt35Turbo(baseUrl: string, apiKey?: string): LiteLLMProvider;
39
+ claude3Opus(baseUrl: string, apiKey?: string): LiteLLMProvider;
40
+ claude3Sonnet(baseUrl: string, apiKey?: string): LiteLLMProvider;
41
+ claude3Haiku(baseUrl: string, apiKey?: string): LiteLLMProvider;
42
+ ollama(baseUrl: string, model: string): LiteLLMProvider;
43
+ };
@@ -0,0 +1,377 @@
1
+ import { appendFileSync, promises as fs, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { merge, Observable } from 'rxjs';
4
+ import { concatWith, filter, map, mergeMap, shareReplay, tap } from 'rxjs/operators';
5
+ import { getLogger } from '../core/logger';
6
+ import { generateEventId } from '../events/utils';
7
+ import { aggregateChoice, aggregateLLMUsage, choices, getContent, splitInlineXml, usage, } from './chat-completions';
8
+ const singleString = (input) => {
9
+ if (!input)
10
+ return undefined;
11
+ if (Array.isArray(input)) {
12
+ return input.join('');
13
+ }
14
+ return input;
15
+ };
16
+ export class LiteLLMProvider {
17
+ config;
18
+ debugLogInitialized = false;
19
+ logger;
20
+ constructor(config) {
21
+ this.config = {
22
+ ...config,
23
+ temperature: config.temperature ?? 0.7,
24
+ maxTokens: config.maxTokens ?? 4096,
25
+ topP: config.topP ?? 1.0,
26
+ timeout: config.timeout ?? 60000,
27
+ extraParams: config.extraParams ?? {},
28
+ };
29
+ this.logger = getLogger({ base: { component: 'LiteLLMProvider' } });
30
+ this.logger.info({
31
+ baseUrl: this.config.baseUrl,
32
+ model: this.config.model,
33
+ temperature: this.config.temperature,
34
+ maxTokens: this.config.maxTokens,
35
+ timeout: this.config.timeout,
36
+ hasApiKey: !!this.config.apiKey,
37
+ }, 'LiteLLM provider initialized');
38
+ }
39
+ call(request) {
40
+ const rawStream$ = this.createSSEStream(request);
41
+ const stream$ = (this.config.debugLogPath
42
+ ? rawStream$.pipe(tap((chunk) => this.debugLogRawChunk(chunk)))
43
+ : rawStream$).pipe(shareReplay());
44
+ const choices$ = stream$.pipe(choices());
45
+ const usage$ = stream$.pipe(usage());
46
+ const { content, tags } = splitInlineXml(choices$.pipe(getContent()));
47
+ let contentIndex = 0;
48
+ const contentDeltas$ = content.pipe(map((delta) => ({
49
+ kind: 'content-delta',
50
+ delta,
51
+ index: contentIndex++,
52
+ timestamp: new Date().toISOString(),
53
+ })), this.debugLog('content-delta'));
54
+ let thoughtIndex = 0;
55
+ const thoughts$ = tags.pipe(filter((tag) => [
56
+ 'thinking',
57
+ 'analysis',
58
+ 'reasoning',
59
+ 'reflection',
60
+ 'planning',
61
+ 'debugging',
62
+ 'decision',
63
+ 'observation',
64
+ 'strategizing',
65
+ ].includes(tag.name)), map((tag) => {
66
+ const thoughtType = singleString(tag.attributes.thoughtType || tag.attributes.thought_type || tag.name) || 'thinking';
67
+ const verbosity = singleString(tag.attributes.verbosity) || 'normal';
68
+ const content = singleString(tag.attributes.content || tag.content) || '';
69
+ return {
70
+ kind: 'thought-stream',
71
+ thoughtId: generateEventId(),
72
+ thoughtType,
73
+ verbosity,
74
+ content,
75
+ index: thoughtIndex++,
76
+ timestamp: new Date().toISOString(),
77
+ metadata: {
78
+ source: 'content-delta',
79
+ },
80
+ };
81
+ }), this.debugLog('thought-stream'));
82
+ const contentComplete$ = choices$.pipe(aggregateChoice(), map((aggregated) => {
83
+ const content = aggregated.delta?.content || '';
84
+ const toolCalls = aggregated.delta?.tool_calls
85
+ ?.filter((tc) => tc.id && tc.function?.name && tc.function?.arguments)
86
+ .map((tc) => ({
87
+ id: tc.id,
88
+ type: 'function',
89
+ function: {
90
+ name: tc.function?.name,
91
+ arguments: (typeof tc.function?.arguments === 'string'
92
+ ? JSON.parse(tc.function?.arguments)
93
+ : tc.function?.arguments) || {},
94
+ },
95
+ }));
96
+ return {
97
+ kind: 'content-complete',
98
+ content,
99
+ toolCalls: toolCalls?.length ? toolCalls : undefined,
100
+ finishReason: aggregated.finish_reason || 'stop',
101
+ timestamp: new Date().toISOString(),
102
+ };
103
+ }), this.debugLog('content-complete'));
104
+ const usageComplete$ = usage$.pipe(aggregateLLMUsage(), map((usage) => ({
105
+ kind: 'llm-usage',
106
+ model: this.config.model,
107
+ ...usage,
108
+ timestamp: new Date().toISOString(),
109
+ })), this.debugLog('llm-usage'));
110
+ const toolCalls$ = contentComplete$.pipe(mergeMap((event) => event.toolCalls?.map((tc) => ({
111
+ kind: 'tool-call',
112
+ toolCallId: tc.id,
113
+ toolName: tc.function.name,
114
+ arguments: tc.function.arguments,
115
+ timestamp: event.timestamp,
116
+ })) || []));
117
+ return merge(contentDeltas$, thoughts$, usageComplete$, toolCalls$).pipe(concatWith(contentComplete$));
118
+ }
119
+ debugLogRawChunk(chunk) {
120
+ if (!this.config.debugLogPath) {
121
+ return;
122
+ }
123
+ if (!this.debugLogInitialized) {
124
+ try {
125
+ mkdirSync(dirname(this.config.debugLogPath), { recursive: true });
126
+ writeFileSync(this.config.debugLogPath, `# LLM Raw Stream Debug Log\n# Started: ${new Date().toISOString()}\n# Model: ${this.config.model}\n\n`, { flag: 'w' });
127
+ this.debugLogInitialized = true;
128
+ }
129
+ catch (error) {
130
+ this.logger.warn({ error }, 'Failed to initialize debug log file');
131
+ return;
132
+ }
133
+ }
134
+ try {
135
+ const logEntry = [
136
+ `chunk: ${new Date().toISOString()}`,
137
+ `data: ${JSON.stringify(chunk)}`,
138
+ '',
139
+ ].join('\n');
140
+ appendFileSync(this.config.debugLogPath, `${logEntry}\n`);
141
+ }
142
+ catch (error) {
143
+ this.logger.warn({ error }, 'Failed to write debug log');
144
+ }
145
+ }
146
+ debugLog(eventType) {
147
+ if (!this.config.debugLogPath) {
148
+ return (source) => source;
149
+ }
150
+ return (source) => source.pipe(tap(async (event) => {
151
+ try {
152
+ if (!this.config.debugLogPath) {
153
+ return;
154
+ }
155
+ if (!this.debugLogInitialized) {
156
+ await fs.mkdir(dirname(this.config.debugLogPath), { recursive: true });
157
+ await fs.writeFile(this.config.debugLogPath, `# LLM Event Debug Log\n# Started: ${new Date().toISOString()}\n# Model: ${this.config.model}\n\n`, { flag: 'w' });
158
+ this.debugLogInitialized = true;
159
+ }
160
+ const logEntry = [
161
+ `event: ${eventType}`,
162
+ `data: ${JSON.stringify(event)}`,
163
+ `when: ${new Date().toISOString()}`,
164
+ '',
165
+ ].join('\n');
166
+ await fs.appendFile(this.config.debugLogPath, `${logEntry}\n`);
167
+ }
168
+ catch (error) {
169
+ this.logger.warn({ error, eventType }, 'Failed to write debug log');
170
+ }
171
+ }));
172
+ }
173
+ createSSEStream(request) {
174
+ return new Observable((subscriber) => {
175
+ const controller = new AbortController();
176
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
177
+ const litellmRequest = {
178
+ model: this.config.model,
179
+ messages: request.messages.map((msg) => {
180
+ const baseMsg = {
181
+ role: msg.role,
182
+ content: msg.content,
183
+ };
184
+ if (msg.name)
185
+ baseMsg.name = msg.name;
186
+ if (msg.toolCallId)
187
+ baseMsg.tool_call_id = msg.toolCallId;
188
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
189
+ baseMsg.tool_calls = msg.toolCalls.map((tc) => ({
190
+ id: tc.id,
191
+ type: tc.type,
192
+ function: {
193
+ name: tc.function.name,
194
+ arguments: typeof tc.function.arguments === 'string'
195
+ ? tc.function.arguments
196
+ : JSON.stringify(tc.function.arguments),
197
+ },
198
+ }));
199
+ }
200
+ return baseMsg;
201
+ }),
202
+ temperature: this.config.temperature,
203
+ max_tokens: this.config.maxTokens,
204
+ top_p: this.config.topP,
205
+ stream: true,
206
+ stream_options: { include_usage: true },
207
+ ...this.config.extraParams,
208
+ };
209
+ if (request.sessionId) {
210
+ litellmRequest.metadata = {
211
+ ...(litellmRequest.metadata || {}),
212
+ session_id: request.sessionId,
213
+ };
214
+ }
215
+ if (request.tools && request.tools.length > 0) {
216
+ litellmRequest.tools = request.tools.map((tool) => ({
217
+ type: 'function',
218
+ function: {
219
+ name: tool.name,
220
+ description: tool.description,
221
+ parameters: tool.parameters,
222
+ },
223
+ }));
224
+ }
225
+ const url = `${this.config.baseUrl}/chat/completions`;
226
+ (async () => {
227
+ try {
228
+ const response = await fetch(url, {
229
+ method: 'POST',
230
+ headers: {
231
+ 'Content-Type': 'application/json',
232
+ ...(this.config.apiKey && {
233
+ Authorization: `Bearer ${this.config.apiKey}`,
234
+ }),
235
+ },
236
+ body: JSON.stringify(litellmRequest),
237
+ signal: controller.signal,
238
+ });
239
+ clearTimeout(timeoutId);
240
+ if (!response.ok) {
241
+ const errorText = await response.text();
242
+ throw new Error(`LiteLLM API error: ${response.status} ${response.statusText} - ${errorText}`);
243
+ }
244
+ if (!response.body) {
245
+ throw new Error('No response body');
246
+ }
247
+ const reader = response.body.getReader();
248
+ const decoder = new TextDecoder();
249
+ let buffer = '';
250
+ while (true) {
251
+ const { done, value } = await reader.read();
252
+ if (done) {
253
+ break;
254
+ }
255
+ buffer += decoder.decode(value, { stream: true });
256
+ const lines = buffer.split('\n');
257
+ buffer = lines.pop() || '';
258
+ for (const line of lines) {
259
+ if (!line.trim() || line.trim() === '')
260
+ continue;
261
+ if (!line.startsWith('data: '))
262
+ continue;
263
+ const data = line.slice(6).trim();
264
+ if (data === '[DONE]')
265
+ continue;
266
+ try {
267
+ const chunk = JSON.parse(data);
268
+ subscriber.next(chunk);
269
+ }
270
+ catch (error) {
271
+ this.logger.warn({ error, line }, 'Failed to parse SSE chunk');
272
+ }
273
+ }
274
+ }
275
+ subscriber.complete();
276
+ }
277
+ catch (error) {
278
+ clearTimeout(timeoutId);
279
+ subscriber.error(error);
280
+ }
281
+ })();
282
+ return () => {
283
+ clearTimeout(timeoutId);
284
+ controller.abort();
285
+ };
286
+ });
287
+ }
288
+ getModel() {
289
+ return this.config.model;
290
+ }
291
+ updateConfig(updates) {
292
+ Object.assign(this.config, updates);
293
+ }
294
+ }
295
+ export const LiteLLM = {
296
+ novaMicro(baseUrl, apiKey) {
297
+ return new LiteLLMProvider({
298
+ baseUrl,
299
+ model: 'amazon.nova-micro-v1:0',
300
+ apiKey,
301
+ temperature: 0.7,
302
+ maxTokens: 8192,
303
+ });
304
+ },
305
+ novaLite(baseUrl, apiKey, debugLogPath) {
306
+ return new LiteLLMProvider({
307
+ baseUrl,
308
+ model: 'amazon.nova-lite-v1:0',
309
+ apiKey,
310
+ temperature: 0.7,
311
+ maxTokens: 8192,
312
+ debugLogPath,
313
+ });
314
+ },
315
+ gpt4(baseUrl, apiKey) {
316
+ return new LiteLLMProvider({
317
+ baseUrl,
318
+ model: 'gpt-4',
319
+ apiKey,
320
+ temperature: 0.7,
321
+ maxTokens: 4096,
322
+ });
323
+ },
324
+ gpt4Turbo(baseUrl, apiKey) {
325
+ return new LiteLLMProvider({
326
+ baseUrl,
327
+ model: 'gpt-4-turbo-preview',
328
+ apiKey,
329
+ temperature: 0.7,
330
+ maxTokens: 4096,
331
+ });
332
+ },
333
+ gpt35Turbo(baseUrl, apiKey) {
334
+ return new LiteLLMProvider({
335
+ baseUrl,
336
+ model: 'gpt-3.5-turbo',
337
+ apiKey,
338
+ temperature: 0.7,
339
+ maxTokens: 4096,
340
+ });
341
+ },
342
+ claude3Opus(baseUrl, apiKey) {
343
+ return new LiteLLMProvider({
344
+ baseUrl,
345
+ model: 'claude-3-opus-20240229',
346
+ apiKey,
347
+ temperature: 0.7,
348
+ maxTokens: 4096,
349
+ });
350
+ },
351
+ claude3Sonnet(baseUrl, apiKey) {
352
+ return new LiteLLMProvider({
353
+ baseUrl,
354
+ model: 'claude-3-sonnet-20240229',
355
+ apiKey,
356
+ temperature: 0.7,
357
+ maxTokens: 4096,
358
+ });
359
+ },
360
+ claude3Haiku(baseUrl, apiKey) {
361
+ return new LiteLLMProvider({
362
+ baseUrl,
363
+ model: 'claude-3-haiku-20240307',
364
+ apiKey,
365
+ temperature: 0.7,
366
+ maxTokens: 4096,
367
+ });
368
+ },
369
+ ollama(baseUrl, model) {
370
+ return new LiteLLMProvider({
371
+ baseUrl,
372
+ model: `ollama/${model}`,
373
+ temperature: 0.7,
374
+ maxTokens: 2048,
375
+ });
376
+ },
377
+ };
@@ -0,0 +1,37 @@
1
+ import type { AnyEvent } from '../types/event';
2
+ export interface BufferedEvent {
3
+ id: string;
4
+ event: AnyEvent;
5
+ timestamp: number;
6
+ }
7
+ export interface EventBufferConfig {
8
+ maxSize?: number;
9
+ ttl?: number;
10
+ autoCleanup?: boolean;
11
+ cleanupInterval?: number;
12
+ }
13
+ export declare class EventBuffer {
14
+ private buffers;
15
+ private eventCounters;
16
+ private cleanupTimer?;
17
+ private readonly maxSize;
18
+ private readonly ttl;
19
+ private readonly autoCleanup;
20
+ private readonly cleanupInterval;
21
+ constructor(config?: EventBufferConfig);
22
+ add(contextId: string, event: AnyEvent): string;
23
+ getEventsSince(contextId: string, lastEventId: string): BufferedEvent[];
24
+ getAll(contextId: string): BufferedEvent[];
25
+ clear(contextId: string): void;
26
+ clearAll(): void;
27
+ cleanup(): number;
28
+ getStats(): {
29
+ contexts: number;
30
+ totalEvents: number;
31
+ averageEventsPerContext: number;
32
+ oldestEventAge: number;
33
+ };
34
+ private startCleanup;
35
+ stopCleanup(): void;
36
+ shutdown(): void;
37
+ }
@@ -0,0 +1,116 @@
1
+ export class EventBuffer {
2
+ buffers = new Map();
3
+ eventCounters = new Map();
4
+ cleanupTimer;
5
+ maxSize;
6
+ ttl;
7
+ autoCleanup;
8
+ cleanupInterval;
9
+ constructor(config = {}) {
10
+ this.maxSize = config.maxSize ?? 100;
11
+ this.ttl = config.ttl ?? 5 * 60 * 1000;
12
+ this.autoCleanup = config.autoCleanup ?? true;
13
+ this.cleanupInterval = config.cleanupInterval ?? 30 * 1000;
14
+ if (this.autoCleanup) {
15
+ this.startCleanup();
16
+ }
17
+ }
18
+ add(contextId, event) {
19
+ const counter = (this.eventCounters.get(contextId) ?? 0) + 1;
20
+ this.eventCounters.set(contextId, counter);
21
+ const id = `${contextId}-${counter}`;
22
+ let buffer = this.buffers.get(contextId);
23
+ if (!buffer) {
24
+ buffer = [];
25
+ this.buffers.set(contextId, buffer);
26
+ }
27
+ buffer.push({
28
+ id,
29
+ event,
30
+ timestamp: Date.now(),
31
+ });
32
+ if (buffer.length > this.maxSize) {
33
+ buffer.shift();
34
+ }
35
+ return id;
36
+ }
37
+ getEventsSince(contextId, lastEventId) {
38
+ const buffer = this.buffers.get(contextId);
39
+ if (!buffer || buffer.length === 0) {
40
+ return [];
41
+ }
42
+ const lastIndex = buffer.findIndex((e) => e.id === lastEventId);
43
+ if (lastIndex === -1) {
44
+ return [];
45
+ }
46
+ return buffer.slice(lastIndex + 1);
47
+ }
48
+ getAll(contextId) {
49
+ return this.buffers.get(contextId) ?? [];
50
+ }
51
+ clear(contextId) {
52
+ this.buffers.delete(contextId);
53
+ this.eventCounters.delete(contextId);
54
+ }
55
+ clearAll() {
56
+ this.buffers.clear();
57
+ this.eventCounters.clear();
58
+ }
59
+ cleanup() {
60
+ const now = Date.now();
61
+ let removedCount = 0;
62
+ for (const [contextId, buffer] of this.buffers.entries()) {
63
+ const originalLength = buffer.length;
64
+ const filtered = buffer.filter((e) => now - e.timestamp < this.ttl);
65
+ if (filtered.length < originalLength) {
66
+ removedCount += originalLength - filtered.length;
67
+ if (filtered.length === 0) {
68
+ this.buffers.delete(contextId);
69
+ }
70
+ else {
71
+ this.buffers.set(contextId, filtered);
72
+ }
73
+ }
74
+ }
75
+ return removedCount;
76
+ }
77
+ getStats() {
78
+ const contexts = this.buffers.size;
79
+ let totalEvents = 0;
80
+ let oldestTimestamp = Date.now();
81
+ for (const buffer of this.buffers.values()) {
82
+ totalEvents += buffer.length;
83
+ if (buffer.length > 0) {
84
+ const oldest = buffer[0].timestamp;
85
+ if (oldest < oldestTimestamp) {
86
+ oldestTimestamp = oldest;
87
+ }
88
+ }
89
+ }
90
+ return {
91
+ contexts,
92
+ totalEvents,
93
+ averageEventsPerContext: contexts > 0 ? totalEvents / contexts : 0,
94
+ oldestEventAge: Date.now() - oldestTimestamp,
95
+ };
96
+ }
97
+ startCleanup() {
98
+ if (this.cleanupTimer) {
99
+ return;
100
+ }
101
+ this.cleanupTimer = setInterval(() => {
102
+ this.cleanup();
103
+ }, this.cleanupInterval);
104
+ this.cleanupTimer.unref?.();
105
+ }
106
+ stopCleanup() {
107
+ if (this.cleanupTimer) {
108
+ clearInterval(this.cleanupTimer);
109
+ this.cleanupTimer = undefined;
110
+ }
111
+ }
112
+ shutdown() {
113
+ this.stopCleanup();
114
+ this.clearAll();
115
+ }
116
+ }
@@ -0,0 +1,31 @@
1
+ import type { AnyEvent } from '../types/event';
2
+ export type EventFilter = (event: AnyEvent) => boolean;
3
+ export interface SubscriptionConfig {
4
+ contextId: string;
5
+ taskId?: string;
6
+ filterInternal?: boolean;
7
+ filter?: EventFilter;
8
+ includeKinds?: string[];
9
+ excludeKinds?: string[];
10
+ }
11
+ export interface Subscriber {
12
+ id: string;
13
+ config: SubscriptionConfig;
14
+ send(event: AnyEvent, eventId: string): void;
15
+ close(): void;
16
+ }
17
+ export declare class EventRouter {
18
+ private subscribers;
19
+ subscribe(subscriber: Subscriber): void;
20
+ unsubscribe(subscriberId: string, contextId: string): void;
21
+ route(contextId: string, event: AnyEvent, eventId: string): number;
22
+ private shouldSendToSubscriber;
23
+ getSubscriberCount(contextId: string): number;
24
+ getActiveContexts(): string[];
25
+ getStats(): {
26
+ totalContexts: number;
27
+ totalSubscribers: number;
28
+ averageSubscribersPerContext: number;
29
+ };
30
+ clear(): void;
31
+ }