profclaw 0.0.1-beta.2 → 2.0.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 (156) hide show
  1. package/CHANGELOG.md +21 -38
  2. package/CONTRIBUTING.md +3 -5
  3. package/README.md +128 -63
  4. package/dist/chat/execution/tools/index.d.ts +1 -1
  5. package/dist/chat/execution/tools/index.js +41 -2
  6. package/dist/chat/providers/slack/index.d.ts +2 -2
  7. package/dist/cli/commands/models.js +39 -26
  8. package/dist/cli/commands/onboard.js +64 -79
  9. package/dist/cli/commands/setup.js +293 -90
  10. package/dist/cli/index.js +0 -6
  11. package/dist/cli/providers.d.ts +15 -0
  12. package/dist/cli/providers.js +41 -0
  13. package/dist/core/deployment.js +2 -0
  14. package/dist/providers/ai-sdk.js +26 -6
  15. package/dist/routes/channels.d.ts +12 -0
  16. package/dist/routes/channels.js +166 -0
  17. package/dist/server/route-loader.js +1 -0
  18. package/dist/server.js +115 -109
  19. package/package.json +1 -1
  20. package/skills/git-workflow/SKILL.md +3 -3
  21. package/ui/dist/assets/{ActivityView-DIPQSJ_d.js → ActivityView-BeshZXmE.js} +1 -1
  22. package/ui/dist/assets/{AgentList-CqyaYrcq.js → AgentList-QVHeldPi.js} +1 -1
  23. package/ui/dist/assets/{AnalyticsDashboard-C-mjuGHT.js → AnalyticsDashboard-C9R97dXE.js} +1 -1
  24. package/ui/dist/assets/{AreaChart-CfYWmbtr.js → AreaChart-B73gaiVQ.js} +1 -1
  25. package/ui/dist/assets/{CartesianChart-BaQ3It0c.js → CartesianChart-CXYL2R-A.js} +1 -1
  26. package/ui/dist/assets/{ChatView-DxxQy6pl.js → ChatView-qjZErlrp.js} +1 -1
  27. package/ui/dist/assets/{CostsDashboard-CDBBcGkg.js → CostsDashboard-O4DCLy1i.js} +1 -1
  28. package/ui/dist/assets/{EstimateSelect-CDjf5qKl.js → EstimateSelect-1hQf4Osk.js} +1 -1
  29. package/ui/dist/assets/{FloatingChatbot-CZHjvJIe.js → FloatingChatbot-BZBzrJxd.js} +1 -1
  30. package/ui/dist/assets/{InviteCodeManagement-DTm-IPTm.js → InviteCodeManagement-LEoGqYeA.js} +1 -1
  31. package/ui/dist/assets/{ProjectDetail-DlYUcOss.js → ProjectDetail--gPfvATB.js} +1 -1
  32. package/ui/dist/assets/{ProjectIcon-BMRpnM04.js → ProjectIcon-Chd_ofsw.js} +1 -1
  33. package/ui/dist/assets/{ProjectList-D85TN0i0.js → ProjectList-BvUfVB6c.js} +1 -1
  34. package/ui/dist/assets/{Settings-CgPqzUvj.js → Settings-1kaf7VHE.js} +1 -1
  35. package/ui/dist/assets/{StatusIndicator-yig0uZEA.js → StatusIndicator-pRfkNcdV.js} +1 -1
  36. package/ui/dist/assets/{SummaryDetail-CwLAfHgJ.js → SummaryDetail-DV3AmONk.js} +1 -1
  37. package/ui/dist/assets/{SummaryList-BvS5Idnu.js → SummaryList-B6kXiTxL.js} +1 -1
  38. package/ui/dist/assets/{TaskDetail-zarC1fOt.js → TaskDetail-CV6CzbOS.js} +1 -1
  39. package/ui/dist/assets/{TaskList-CyJq3ANE.js → TaskList-DL7brQKd.js} +1 -1
  40. package/ui/dist/assets/{TicketBoard-BlWr3-iH.js → TicketBoard-CrkkFj7C.js} +1 -1
  41. package/ui/dist/assets/{TicketDetail-DmqWKj2W.js → TicketDetail-hnnYclpD.js} +1 -1
  42. package/ui/dist/assets/{TicketList-Scj4pMWa.js → TicketList-CsDAyQCL.js} +1 -1
  43. package/ui/dist/assets/{UserManagement-BH7uG7dw.js → UserManagement-BGFQyTSr.js} +1 -1
  44. package/ui/dist/assets/{ViewSwitcher-3HzZUdWn.js → ViewSwitcher-CnL0OqA8.js} +1 -1
  45. package/ui/dist/assets/{alert-dialog-DWPb2IfQ.js → alert-dialog-SKM6Gjz4.js} +1 -1
  46. package/ui/dist/assets/{archive-CGck7gxR.js → archive-BsLa7brF.js} +1 -1
  47. package/ui/dist/assets/{badge-DEmc3Z_l.js → badge-CVHH1a-v.js} +1 -1
  48. package/ui/dist/assets/{brain-CfFdLVqb.js → brain-6o_RHVpe.js} +1 -1
  49. package/ui/dist/assets/{bug-DqSGPUUE.js → bug-CmZI7jqM.js} +1 -1
  50. package/ui/dist/assets/{calendar-BuZ4zbPW.js → calendar-Bf0t2B82.js} +1 -1
  51. package/ui/dist/assets/{chevron-up-DcJ4LL_N.js → chevron-up-CoUsOvZj.js} +1 -1
  52. package/ui/dist/assets/{circle-x-C78PNZjF.js → circle-x-Dgw11LgT.js} +1 -1
  53. package/ui/dist/assets/{constants-DhDjZe4P.js → constants-Q5Zivl-a.js} +1 -1
  54. package/ui/dist/assets/{cpu-Ck6AoG8c.js → cpu-DiPMQICV.js} +1 -1
  55. package/ui/dist/assets/{database-CA2w8Lvt.js → database-BwTfupDi.js} +1 -1
  56. package/ui/dist/assets/{dialog-BOdGWhgE.js → dialog-BUqN9Psj.js} +1 -1
  57. package/ui/dist/assets/{ellipsis-FhHKRbfl.js → ellipsis-UJx4Xnf6.js} +1 -1
  58. package/ui/dist/assets/{ellipsis-vertical-CO0UqE4-.js → ellipsis-vertical-DoULYEKn.js} +1 -1
  59. package/ui/dist/assets/{file-code-ChnhYYj1.js → file-code-B2UrJLwi.js} +1 -1
  60. package/ui/dist/assets/{folder-open-CHwA1-ve.js → folder-open-BJi577JH.js} +1 -1
  61. package/ui/dist/assets/{funnel-ByEjRFNB.js → funnel-PkaD_HSR.js} +1 -1
  62. package/ui/dist/assets/{git-branch-GTOPQgU4.js → git-branch-C_K30am0.js} +1 -1
  63. package/ui/dist/assets/{git-commit-horizontal-F5M84Kut.js → git-commit-horizontal-IT6ecxWQ.js} +1 -1
  64. package/ui/dist/assets/{globe-CQWfr7hT.js → globe-f-jjCa_g.js} +1 -1
  65. package/ui/dist/assets/{hash-nKC9gtLg.js → hash-DNEv8Elo.js} +1 -1
  66. package/ui/dist/assets/{history-DfFSA9xJ.js → history-BQSBo_1Q.js} +1 -1
  67. package/ui/dist/assets/{index-DHGvP9mp.js → index-BUmnwPQr.js} +1 -1
  68. package/ui/dist/assets/{index-Z4-RtZcs.js → index-BfF5G4Cw.js} +1 -1
  69. package/ui/dist/assets/{index-DeI8lKRR.js → index-C8N375c2.js} +1 -1
  70. package/ui/dist/assets/index-CuxnqVVz.css +1 -0
  71. package/ui/dist/assets/{index-Ca3vk7Gq.js → index-CzMM3QOk.js} +1 -1
  72. package/ui/dist/assets/{index--JkIxVXb.js → index-lJlNPFVJ.js} +3 -3
  73. package/ui/dist/assets/{index-a4Fxq0Hb.js → index-nS3EErDg.js} +1 -1
  74. package/ui/dist/assets/{layers-DFeLExSa.js → layers-jFqVrWMD.js} +1 -1
  75. package/ui/dist/assets/{layout-dashboard-WmgjMhWF.js → layout-dashboard-CcKt1hXw.js} +1 -1
  76. package/ui/dist/assets/{lightbulb-Bo8hEr7B.js → lightbulb-Jhp7FvbU.js} +1 -1
  77. package/ui/dist/assets/{link-2-YqrpQXBH.js → link-2-DdO7ostF.js} +1 -1
  78. package/ui/dist/assets/{markdown-renderer-Croje6Xe.js → markdown-renderer-n1ToC4XQ.js} +1 -1
  79. package/ui/dist/assets/{message-square-CD0H5hot.js → message-square-CBgH65X7.js} +1 -1
  80. package/ui/dist/assets/{pencil-DpFnj5dn.js → pencil-CYXCEPQh.js} +1 -1
  81. package/ui/dist/assets/{play-DVgea_jT.js → play-d1ONaH0-.js} +1 -1
  82. package/ui/dist/assets/{popover-JXecfWG5.js → popover-CrmZgoWu.js} +1 -1
  83. package/ui/dist/assets/{progress-C1ghf1Fu.js → progress-uv4SPkL5.js} +1 -1
  84. package/ui/dist/assets/{rocket-BFxPYGx9.js → rocket-DhvUetsk.js} +1 -1
  85. package/ui/dist/assets/{select-MADP8m6Q.js → select-CeQ8AHGy.js} +1 -1
  86. package/ui/dist/assets/{send-eYnW_7n-.js → send-P0r5D7td.js} +1 -1
  87. package/ui/dist/assets/{server-CPMaysH8.js → server-CVHTZB72.js} +1 -1
  88. package/ui/dist/assets/{settings-2-scXUOuE_.js → settings-2-ocj5uHGx.js} +1 -1
  89. package/ui/dist/assets/{sheet-r6NSOxi-.js → sheet-DuQExSho.js} +1 -1
  90. package/ui/dist/assets/{shield-check-PEr7nmVw.js → shield-check-DfVdE34l.js} +1 -1
  91. package/ui/dist/assets/{shield-off-CLtXio62.js → shield-off-8l0J6R-8.js} +1 -1
  92. package/ui/dist/assets/{skeleton-C9oEzNlU.js → skeleton-TFiFJ3nB.js} +1 -1
  93. package/ui/dist/assets/{smartphone-DusFfY0U.js → smartphone-77_g3SQn.js} +1 -1
  94. package/ui/dist/assets/{square-e1yxB2eR.js → square-Ckzsr3Rn.js} +1 -1
  95. package/ui/dist/assets/{switch-tNs0n-kz.js → switch-CAePkd6k.js} +1 -1
  96. package/ui/dist/assets/{table-CXxtLicg.js → table-ZjAZ7hkt.js} +1 -1
  97. package/ui/dist/assets/{tag-tAwMmQ9G.js → tag-C35mgoEv.js} +1 -1
  98. package/ui/dist/assets/{textarea-BjiEeIWh.js → textarea-DsVPnSgR.js} +1 -1
  99. package/ui/dist/assets/{timer-Vel1N3RP.js → timer-DHJBS2w1.js} +1 -1
  100. package/ui/dist/assets/{tooltip-ZMwtfr1J.js → tooltip-BYmQ331W.js} +1 -1
  101. package/ui/dist/assets/{trash-2-eM-CNwsZ.js → trash-2-keO5tKPE.js} +1 -1
  102. package/ui/dist/assets/{trending-up-BGafmjRo.js → trending-up-BBv8KbrY.js} +1 -1
  103. package/ui/dist/assets/{wand-sparkles-DPvcd9Pi.js → wand-sparkles-BusQ52ZK.js} +1 -1
  104. package/ui/dist/assets/{wrench-B8s3zYVV.js → wrench-02YaOSiK.js} +1 -1
  105. package/ui/dist/index.html +2 -2
  106. package/dist/chat/auto-reply.d.ts +0 -90
  107. package/dist/chat/auto-reply.js +0 -272
  108. package/dist/chat/execution/pty.d.ts +0 -92
  109. package/dist/chat/execution/pty.js +0 -285
  110. package/dist/chat/execution/tools/glinr-ops.d.ts +0 -202
  111. package/dist/chat/execution/tools/glinr-ops.js +0 -694
  112. package/dist/cli/commands/browser.d.ts +0 -3
  113. package/dist/cli/commands/browser.js +0 -138
  114. package/dist/cli/commands/canvas.d.ts +0 -3
  115. package/dist/cli/commands/canvas.js +0 -155
  116. package/dist/cli/commands/nodes.d.ts +0 -3
  117. package/dist/cli/commands/nodes.js +0 -193
  118. package/dist/diagnostics/exporters.d.ts +0 -21
  119. package/dist/diagnostics/exporters.js +0 -138
  120. package/dist/diagnostics/index.d.ts +0 -10
  121. package/dist/diagnostics/index.js +0 -9
  122. package/dist/diagnostics/middleware.d.ts +0 -12
  123. package/dist/diagnostics/middleware.js +0 -58
  124. package/dist/diagnostics/tracer.d.ts +0 -133
  125. package/dist/diagnostics/tracer.js +0 -257
  126. package/dist/discovery/index.d.ts +0 -8
  127. package/dist/discovery/index.js +0 -7
  128. package/dist/discovery/mdns.d.ts +0 -40
  129. package/dist/discovery/mdns.js +0 -249
  130. package/dist/plugins/registry.d.ts +0 -122
  131. package/dist/plugins/registry.js +0 -352
  132. package/dist/plugins/sandbox.d.ts +0 -77
  133. package/dist/plugins/sandbox.js +0 -287
  134. package/dist/plugins/search/brave.d.ts +0 -9
  135. package/dist/plugins/search/brave.js +0 -157
  136. package/dist/plugins/search/duckduckgo.d.ts +0 -9
  137. package/dist/plugins/search/duckduckgo.js +0 -171
  138. package/dist/plugins/search/index.d.ts +0 -13
  139. package/dist/plugins/search/index.js +0 -23
  140. package/dist/plugins/search/searxng.d.ts +0 -9
  141. package/dist/plugins/search/searxng.js +0 -170
  142. package/dist/plugins/search/serper.d.ts +0 -9
  143. package/dist/plugins/search/serper.js +0 -135
  144. package/dist/plugins/search/tavily.d.ts +0 -9
  145. package/dist/plugins/search/tavily.js +0 -146
  146. package/dist/providers/adapters/anthropic.d.ts +0 -26
  147. package/dist/providers/adapters/anthropic.js +0 -306
  148. package/dist/providers/adapters/base.d.ts +0 -53
  149. package/dist/providers/adapters/base.js +0 -99
  150. package/dist/providers/adapters/ollama.d.ts +0 -30
  151. package/dist/providers/adapters/ollama.js +0 -292
  152. package/dist/utils/ai-cache.d.ts +0 -72
  153. package/dist/utils/ai-cache.js +0 -204
  154. package/dist/utils/file-lock.d.ts +0 -85
  155. package/dist/utils/file-lock.js +0 -204
  156. package/ui/dist/assets/index-WclMPci1.css +0 -1
@@ -1,99 +0,0 @@
1
- /**
2
- * Base Provider Adapter
3
- *
4
- * Abstract base class for AI provider implementations.
5
- * Handles common functionality like configuration and model management.
6
- */
7
- import { randomUUID } from 'node:crypto';
8
- export class BaseProvider {
9
- config;
10
- models = [];
11
- constructor(config) {
12
- this.config = this.createDefaultConfig(config);
13
- }
14
- // === AIProvider Interface ===
15
- configure(config) {
16
- this.config = { ...this.config, ...config };
17
- }
18
- getConfig() {
19
- return { ...this.config };
20
- }
21
- async listModels() {
22
- if (this.models.length === 0) {
23
- this.models = this.getDefaultModels();
24
- }
25
- return [...this.models];
26
- }
27
- getModel(modelId) {
28
- return this.models.find((m) => m.id === modelId);
29
- }
30
- async complete(request) {
31
- const model = request.model || this.config.defaultModel || this.models[0]?.id;
32
- if (!model) {
33
- throw new Error(`No model specified and no default model configured for ${this.name}`);
34
- }
35
- const finalRequest = {
36
- ...request,
37
- provider: this.type,
38
- model,
39
- };
40
- return this.doComplete(finalRequest);
41
- }
42
- async completeStream(request, onChunk) {
43
- const model = request.model || this.config.defaultModel || this.models[0]?.id;
44
- if (!model) {
45
- throw new Error(`No model specified and no default model configured for ${this.name}`);
46
- }
47
- const finalRequest = {
48
- ...request,
49
- provider: this.type,
50
- model,
51
- stream: true,
52
- };
53
- return this.doCompleteStream(finalRequest, onChunk);
54
- }
55
- // === Helper Methods ===
56
- /**
57
- * Create a chat message
58
- */
59
- createMessage(role, content, options) {
60
- return {
61
- id: randomUUID(),
62
- role,
63
- content,
64
- timestamp: new Date().toISOString(),
65
- ...options,
66
- };
67
- }
68
- /**
69
- * Convert messages to provider-specific format
70
- */
71
- formatMessages(messages, systemPrompt) {
72
- const formatted = [];
73
- // Add system prompt if provided
74
- if (systemPrompt) {
75
- formatted.push({ role: 'system', content: systemPrompt });
76
- }
77
- // Add conversation messages
78
- for (const msg of messages) {
79
- formatted.push({
80
- role: msg.role,
81
- content: msg.content,
82
- });
83
- }
84
- return formatted;
85
- }
86
- /**
87
- * Calculate cost from token usage
88
- */
89
- calculateCost(promptTokens, completionTokens, modelId) {
90
- const model = modelId ? this.getModel(modelId) : this.models.find((m) => m.isDefault);
91
- if (!model?.cost) {
92
- return 0;
93
- }
94
- const inputCost = (promptTokens / 1_000_000) * model.cost.inputPerMillion;
95
- const outputCost = (completionTokens / 1_000_000) * model.cost.outputPerMillion;
96
- return inputCost + outputCost;
97
- }
98
- }
99
- //# sourceMappingURL=base.js.map
@@ -1,30 +0,0 @@
1
- /**
2
- * Ollama Provider Adapter
3
- *
4
- * Local LLM inference via Ollama.
5
- * Supports model auto-discovery and streaming.
6
- *
7
- * @see https://ollama.ai/
8
- */
9
- import type { ChatCompletionRequest, ChatCompletionResponse, ModelDefinition, ProviderConfig, ProviderHealth, ProviderType } from '../types.js';
10
- import { BaseProvider } from './base.js';
11
- export declare class OllamaProvider extends BaseProvider {
12
- readonly type: ProviderType;
13
- readonly name = "Ollama";
14
- private discoveredModels;
15
- protected createDefaultConfig(partial: Partial<ProviderConfig>): ProviderConfig;
16
- protected getDefaultModels(): ModelDefinition[];
17
- /**
18
- * Discover models from local Ollama instance
19
- */
20
- discoverModels(): Promise<ModelDefinition[]>;
21
- listModels(): Promise<ModelDefinition[]>;
22
- healthCheck(): Promise<ProviderHealth>;
23
- protected doComplete(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
24
- protected doCompleteStream(request: ChatCompletionRequest, onChunk: (chunk: string) => void): Promise<ChatCompletionResponse>;
25
- }
26
- /**
27
- * Create an Ollama provider instance
28
- */
29
- export declare function createOllamaProvider(config?: Partial<ProviderConfig>): OllamaProvider;
30
- //# sourceMappingURL=ollama.d.ts.map
@@ -1,292 +0,0 @@
1
- /**
2
- * Ollama Provider Adapter
3
- *
4
- * Local LLM inference via Ollama.
5
- * Supports model auto-discovery and streaming.
6
- *
7
- * @see https://ollama.ai/
8
- */
9
- import { randomUUID } from 'node:crypto';
10
- import { BaseProvider } from './base.js';
11
- import { logger } from '../../utils/logger.js';
12
- export class OllamaProvider extends BaseProvider {
13
- type = 'ollama';
14
- name = 'Ollama';
15
- discoveredModels = [];
16
- createDefaultConfig(partial) {
17
- return {
18
- id: 'ollama',
19
- type: 'ollama',
20
- name: 'Ollama',
21
- enabled: true,
22
- baseUrl: partial.baseUrl || process.env.OLLAMA_BASE_URL || 'http://localhost:11434',
23
- defaultModel: partial.defaultModel || process.env.OLLAMA_MODEL || 'llama3.2',
24
- maxConcurrent: partial.maxConcurrent || 3,
25
- timeout: partial.timeout || 300000,
26
- models: [],
27
- ...partial,
28
- };
29
- }
30
- getDefaultModels() {
31
- // Default models that are commonly available
32
- return [
33
- {
34
- id: 'llama3.2',
35
- name: 'Llama 3.2',
36
- provider: 'ollama',
37
- capabilities: ['text', 'code'],
38
- contextWindow: 128000,
39
- maxOutputTokens: 8192,
40
- supportsStreaming: true,
41
- supportsVision: false,
42
- cost: { inputPerMillion: 0, outputPerMillion: 0 },
43
- isDefault: true,
44
- },
45
- {
46
- id: 'deepseek-r1:7b',
47
- name: 'DeepSeek R1 7B',
48
- provider: 'ollama',
49
- capabilities: ['text', 'code', 'reasoning'],
50
- contextWindow: 128000,
51
- maxOutputTokens: 8192,
52
- supportsStreaming: true,
53
- supportsVision: false,
54
- cost: { inputPerMillion: 0, outputPerMillion: 0 },
55
- isDefault: false,
56
- },
57
- {
58
- id: 'qwen2.5:14b',
59
- name: 'Qwen 2.5 14B',
60
- provider: 'ollama',
61
- capabilities: ['text', 'code'],
62
- contextWindow: 128000,
63
- maxOutputTokens: 8192,
64
- supportsStreaming: true,
65
- supportsVision: false,
66
- cost: { inputPerMillion: 0, outputPerMillion: 0 },
67
- isDefault: false,
68
- },
69
- ];
70
- }
71
- /**
72
- * Discover models from local Ollama instance
73
- */
74
- async discoverModels() {
75
- try {
76
- const response = await fetch(`${this.config.baseUrl}/api/tags`, {
77
- signal: AbortSignal.timeout(5000),
78
- });
79
- if (!response.ok) {
80
- logger.warn(`[Ollama] Failed to discover models: ${response.status}`);
81
- return [];
82
- }
83
- const data = (await response.json());
84
- if (!data.models || data.models.length === 0) {
85
- return [];
86
- }
87
- this.discoveredModels = data.models.map((model) => {
88
- const modelId = model.name;
89
- const isReasoning = modelId.toLowerCase().includes('r1') ||
90
- modelId.toLowerCase().includes('reasoning') ||
91
- modelId.toLowerCase().includes('deepseek');
92
- const isVision = modelId.toLowerCase().includes('vision') ||
93
- modelId.toLowerCase().includes('llava');
94
- return {
95
- id: modelId,
96
- name: model.details?.family
97
- ? `${model.details.family} ${model.details.parameter_size || ''}`
98
- : modelId,
99
- provider: 'ollama',
100
- capabilities: isReasoning
101
- ? ['text', 'code', 'reasoning']
102
- : isVision
103
- ? ['text', 'vision']
104
- : ['text', 'code'],
105
- contextWindow: 128000,
106
- maxOutputTokens: 8192,
107
- supportsStreaming: true,
108
- supportsVision: isVision,
109
- cost: { inputPerMillion: 0, outputPerMillion: 0 }, // Free!
110
- isDefault: false,
111
- };
112
- });
113
- logger.info(`[Ollama] Discovered ${this.discoveredModels.length} models`);
114
- return this.discoveredModels;
115
- }
116
- catch (error) {
117
- logger.warn(`[Ollama] Model discovery failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
118
- return [];
119
- }
120
- }
121
- async listModels() {
122
- // Try to discover models first
123
- const discovered = await this.discoverModels();
124
- if (discovered.length > 0) {
125
- this.models = discovered;
126
- return this.models;
127
- }
128
- // Fall back to defaults
129
- this.models = this.getDefaultModels();
130
- return this.models;
131
- }
132
- async healthCheck() {
133
- const startTime = Date.now();
134
- try {
135
- const response = await fetch(`${this.config.baseUrl}/api/tags`, {
136
- signal: AbortSignal.timeout(5000),
137
- });
138
- const latencyMs = Date.now() - startTime;
139
- if (!response.ok) {
140
- return {
141
- provider: 'ollama',
142
- healthy: false,
143
- latencyMs,
144
- message: `Ollama API returned ${response.status}`,
145
- lastChecked: new Date().toISOString(),
146
- };
147
- }
148
- const data = (await response.json());
149
- const modelNames = data.models?.map((m) => m.name) || [];
150
- // Check if default model is available
151
- const defaultModel = this.config.defaultModel || 'llama3.2';
152
- const modelAvailable = modelNames.some((name) => name === defaultModel || name.startsWith(`${defaultModel}:`));
153
- return {
154
- provider: 'ollama',
155
- healthy: true,
156
- latencyMs,
157
- message: modelAvailable
158
- ? `Ollama running with ${modelNames.length} models`
159
- : `Model '${defaultModel}' not found`,
160
- availableModels: modelNames,
161
- lastChecked: new Date().toISOString(),
162
- };
163
- }
164
- catch (error) {
165
- return {
166
- provider: 'ollama',
167
- healthy: false,
168
- latencyMs: Date.now() - startTime,
169
- message: error instanceof Error ? error.message : 'Connection failed',
170
- lastChecked: new Date().toISOString(),
171
- };
172
- }
173
- }
174
- async doComplete(request) {
175
- const startTime = Date.now();
176
- const model = request.model || this.config.defaultModel || 'llama3.2';
177
- const messages = this.formatMessages(request.messages, request.systemPrompt);
178
- const response = await fetch(`${this.config.baseUrl}/api/chat`, {
179
- method: 'POST',
180
- headers: { 'Content-Type': 'application/json' },
181
- body: JSON.stringify({
182
- model,
183
- messages,
184
- stream: false,
185
- options: {
186
- temperature: request.temperature || 0.7,
187
- num_predict: request.maxTokens || 4096,
188
- },
189
- }),
190
- signal: AbortSignal.timeout(this.config.timeout),
191
- });
192
- if (!response.ok) {
193
- const errorText = await response.text();
194
- throw new Error(`Ollama API error: ${response.status} - ${errorText}`);
195
- }
196
- const result = (await response.json());
197
- const duration = Date.now() - startTime;
198
- const promptTokens = result.prompt_eval_count || 0;
199
- const completionTokens = result.eval_count || 0;
200
- return {
201
- id: randomUUID(),
202
- provider: 'ollama',
203
- model,
204
- message: this.createMessage('assistant', result.message?.content || ''),
205
- finishReason: 'stop',
206
- usage: {
207
- promptTokens,
208
- completionTokens,
209
- totalTokens: promptTokens + completionTokens,
210
- cost: 0, // Free!
211
- },
212
- duration,
213
- };
214
- }
215
- async doCompleteStream(request, onChunk) {
216
- const startTime = Date.now();
217
- const model = request.model || this.config.defaultModel || 'llama3.2';
218
- const messages = this.formatMessages(request.messages, request.systemPrompt);
219
- const response = await fetch(`${this.config.baseUrl}/api/chat`, {
220
- method: 'POST',
221
- headers: { 'Content-Type': 'application/json' },
222
- body: JSON.stringify({
223
- model,
224
- messages,
225
- stream: true,
226
- options: {
227
- temperature: request.temperature || 0.7,
228
- num_predict: request.maxTokens || 4096,
229
- },
230
- }),
231
- signal: AbortSignal.timeout(this.config.timeout),
232
- });
233
- if (!response.ok) {
234
- const errorText = await response.text();
235
- throw new Error(`Ollama API error: ${response.status} - ${errorText}`);
236
- }
237
- // Process streaming response
238
- let fullContent = '';
239
- let promptTokens = 0;
240
- let completionTokens = 0;
241
- const reader = response.body?.getReader();
242
- if (!reader) {
243
- throw new Error('No response body');
244
- }
245
- const decoder = new TextDecoder();
246
- while (true) {
247
- const { done, value } = await reader.read();
248
- if (done)
249
- break;
250
- const chunk = decoder.decode(value, { stream: true });
251
- const lines = chunk.split('\n').filter((line) => line.trim());
252
- for (const line of lines) {
253
- try {
254
- const data = JSON.parse(line);
255
- if (data.message?.content) {
256
- fullContent += data.message.content;
257
- onChunk(data.message.content);
258
- }
259
- if (data.done) {
260
- promptTokens = data.prompt_eval_count || 0;
261
- completionTokens = data.eval_count || 0;
262
- }
263
- }
264
- catch {
265
- // Skip invalid JSON
266
- }
267
- }
268
- }
269
- const duration = Date.now() - startTime;
270
- return {
271
- id: randomUUID(),
272
- provider: 'ollama',
273
- model,
274
- message: this.createMessage('assistant', fullContent),
275
- finishReason: 'stop',
276
- usage: {
277
- promptTokens,
278
- completionTokens,
279
- totalTokens: promptTokens + completionTokens,
280
- cost: 0,
281
- },
282
- duration,
283
- };
284
- }
285
- }
286
- /**
287
- * Create an Ollama provider instance
288
- */
289
- export function createOllamaProvider(config) {
290
- return new OllamaProvider(config || {});
291
- }
292
- //# sourceMappingURL=ollama.js.map
@@ -1,72 +0,0 @@
1
- /**
2
- * AI Inference Cache Utility
3
- *
4
- * Provides caching layer for AI inference results to avoid
5
- * redundant API calls for identical inputs.
6
- *
7
- * Cache key = SHA256(cacheType + model + normalized_input)
8
- */
9
- export type CacheType = 'categorize' | 'suggest' | 'summarize' | 'analyze_comment' | 'generate_response';
10
- export interface CacheEntry<T> {
11
- id: string;
12
- cacheType: CacheType;
13
- inputHash: string;
14
- model: string;
15
- provider?: string;
16
- response: T;
17
- confidence?: number;
18
- hitCount: number;
19
- lastHitAt?: Date;
20
- expiresAt?: Date;
21
- createdAt: Date;
22
- updatedAt: Date;
23
- }
24
- export interface CacheStats {
25
- totalEntries: number;
26
- hitCount: number;
27
- missCount: number;
28
- hitRate: number;
29
- byType: Record<string, {
30
- entries: number;
31
- hits: number;
32
- }>;
33
- }
34
- /**
35
- * Generate a consistent hash for cache lookup
36
- */
37
- export declare function generateCacheKey(cacheType: CacheType, model: string, ...inputs: (string | undefined)[]): string;
38
- /**
39
- * Get cached response if available and not expired
40
- */
41
- export declare function getCachedResponse<T>(db: any, // Database instance
42
- cacheType: CacheType, model: string, ...inputs: (string | undefined)[]): Promise<T | null>;
43
- /**
44
- * Store response in cache
45
- */
46
- export declare function setCachedResponse<T>(db: any, cacheType: CacheType, model: string, response: T, options: {
47
- inputs: (string | undefined)[];
48
- provider?: string;
49
- confidence?: number;
50
- ttlMs?: number;
51
- }): Promise<void>;
52
- /**
53
- * Invalidate cache entries by type or specific hash
54
- */
55
- export declare function invalidateCache(db: any, options: {
56
- cacheType?: CacheType;
57
- inputHash?: string;
58
- olderThanMs?: number;
59
- }): Promise<number>;
60
- /**
61
- * Clean up expired cache entries
62
- */
63
- export declare function cleanupExpiredCache(db: any): Promise<number>;
64
- /**
65
- * Get cache statistics
66
- */
67
- export declare function getCacheStats(db: any): Promise<CacheStats>;
68
- /**
69
- * Reset in-memory stats (for testing)
70
- */
71
- export declare function resetCacheStats(): void;
72
- //# sourceMappingURL=ai-cache.d.ts.map
@@ -1,204 +0,0 @@
1
- /**
2
- * AI Inference Cache Utility
3
- *
4
- * Provides caching layer for AI inference results to avoid
5
- * redundant API calls for identical inputs.
6
- *
7
- * Cache key = SHA256(cacheType + model + normalized_input)
8
- */
9
- import { createHash } from 'crypto';
10
- import { logger } from './logger.js';
11
- // Default cache TTL: 7 days for most operations
12
- const DEFAULT_CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
13
- // Cache TTLs by type (in milliseconds)
14
- const CACHE_TTL_BY_TYPE = {
15
- categorize: 30 * 24 * 60 * 60 * 1000, // 30 days - categorization is stable
16
- suggest: 7 * 24 * 60 * 60 * 1000, // 7 days - suggestions can evolve
17
- summarize: 14 * 24 * 60 * 60 * 1000, // 14 days - summaries are fairly stable
18
- analyze_comment: 7 * 24 * 60 * 60 * 1000, // 7 days
19
- generate_response: 1 * 24 * 60 * 60 * 1000, // 1 day - responses should be fresh
20
- };
21
- // In-memory stats tracking
22
- let cacheHits = 0;
23
- let cacheMisses = 0;
24
- /**
25
- * Generate a consistent hash for cache lookup
26
- */
27
- export function generateCacheKey(cacheType, model, ...inputs) {
28
- // Normalize inputs: lowercase, trim, remove extra whitespace
29
- const normalizedInputs = inputs
30
- .filter((i) => !!i)
31
- .map((i) => i.toLowerCase().trim().replace(/\s+/g, ' '))
32
- .join('|');
33
- const keyData = `${cacheType}:${model}:${normalizedInputs}`;
34
- return createHash('sha256').update(keyData).digest('hex');
35
- }
36
- /**
37
- * Get cached response if available and not expired
38
- */
39
- export async function getCachedResponse(db, // Database instance
40
- cacheType, model, ...inputs) {
41
- try {
42
- const inputHash = generateCacheKey(cacheType, model, ...inputs);
43
- const now = Date.now();
44
- const result = await db.query(`SELECT id, response, expires_at, confidence
45
- FROM ai_inference_cache
46
- WHERE cache_type = ? AND input_hash = ? AND model = ?`, [cacheType, inputHash, model]);
47
- if (!result || result.length === 0) {
48
- cacheMisses++;
49
- return null;
50
- }
51
- const entry = result[0];
52
- // Check expiration
53
- if (entry.expires_at && entry.expires_at < now) {
54
- cacheMisses++;
55
- // Clean up expired entry
56
- await db.execute(`DELETE FROM ai_inference_cache WHERE id = ?`, [entry.id]);
57
- logger.debug('[AI Cache] Entry expired', { cacheType, inputHash: inputHash.slice(0, 8) });
58
- return null;
59
- }
60
- // Update hit stats
61
- await db.execute(`UPDATE ai_inference_cache SET hit_count = hit_count + 1, last_hit_at = ? WHERE id = ?`, [now, entry.id]);
62
- cacheHits++;
63
- logger.debug('[AI Cache] Cache hit', { cacheType, inputHash: inputHash.slice(0, 8) });
64
- // Parse and return response
65
- const response = typeof entry.response === 'string'
66
- ? JSON.parse(entry.response)
67
- : entry.response;
68
- return response;
69
- }
70
- catch (error) {
71
- logger.warn('[AI Cache] Error reading cache', { error });
72
- cacheMisses++;
73
- return null;
74
- }
75
- }
76
- /**
77
- * Store response in cache
78
- */
79
- export async function setCachedResponse(db, cacheType, model, response, options) {
80
- try {
81
- const inputHash = generateCacheKey(cacheType, model, ...options.inputs);
82
- const id = `cache_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
83
- const now = Date.now();
84
- const ttl = options.ttlMs ?? CACHE_TTL_BY_TYPE[cacheType] ?? DEFAULT_CACHE_TTL_MS;
85
- const expiresAt = ttl > 0 ? now + ttl : null;
86
- // Upsert: delete existing then insert
87
- await db.execute(`DELETE FROM ai_inference_cache WHERE cache_type = ? AND input_hash = ? AND model = ?`, [cacheType, inputHash, model]);
88
- await db.execute(`INSERT INTO ai_inference_cache
89
- (id, cache_type, input_hash, model, provider, response, confidence, hit_count, expires_at, created_at, updated_at)
90
- VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)`, [
91
- id,
92
- cacheType,
93
- inputHash,
94
- model,
95
- options.provider ?? null,
96
- JSON.stringify(response),
97
- options.confidence ?? null,
98
- expiresAt,
99
- now,
100
- now,
101
- ]);
102
- logger.debug('[AI Cache] Stored response', { cacheType, inputHash: inputHash.slice(0, 8) });
103
- }
104
- catch (error) {
105
- logger.warn('[AI Cache] Error storing cache', { error });
106
- }
107
- }
108
- /**
109
- * Invalidate cache entries by type or specific hash
110
- */
111
- export async function invalidateCache(db, options) {
112
- try {
113
- const conditions = [];
114
- const params = [];
115
- if (options.cacheType) {
116
- conditions.push('cache_type = ?');
117
- params.push(options.cacheType);
118
- }
119
- if (options.inputHash) {
120
- conditions.push('input_hash = ?');
121
- params.push(options.inputHash);
122
- }
123
- if (options.olderThanMs) {
124
- const cutoff = Date.now() - options.olderThanMs;
125
- conditions.push('created_at < ?');
126
- params.push(cutoff);
127
- }
128
- if (conditions.length === 0) {
129
- return 0;
130
- }
131
- const result = await db.execute(`DELETE FROM ai_inference_cache WHERE ${conditions.join(' AND ')}`, params);
132
- const deleted = result?.rowsAffected ?? 0;
133
- logger.info('[AI Cache] Invalidated entries', { deleted, ...options });
134
- return deleted;
135
- }
136
- catch (error) {
137
- logger.warn('[AI Cache] Error invalidating cache', { error });
138
- return 0;
139
- }
140
- }
141
- /**
142
- * Clean up expired cache entries
143
- */
144
- export async function cleanupExpiredCache(db) {
145
- try {
146
- const now = Date.now();
147
- const result = await db.execute(`DELETE FROM ai_inference_cache WHERE expires_at IS NOT NULL AND expires_at < ?`, [now]);
148
- const deleted = result?.rowsAffected ?? 0;
149
- if (deleted > 0) {
150
- logger.info('[AI Cache] Cleaned up expired entries', { deleted });
151
- }
152
- return deleted;
153
- }
154
- catch (error) {
155
- logger.warn('[AI Cache] Error cleaning up cache', { error });
156
- return 0;
157
- }
158
- }
159
- /**
160
- * Get cache statistics
161
- */
162
- export async function getCacheStats(db) {
163
- try {
164
- const totalResult = await db.query(`SELECT COUNT(*) as count FROM ai_inference_cache`);
165
- const totalEntries = totalResult[0]?.count ?? 0;
166
- const byTypeResult = await db.query(`SELECT cache_type, COUNT(*) as entries, SUM(hit_count) as hits
167
- FROM ai_inference_cache
168
- GROUP BY cache_type`);
169
- const byType = {};
170
- for (const row of byTypeResult) {
171
- byType[row.cache_type] = { entries: row.entries, hits: row.hits ?? 0 };
172
- }
173
- const totalHits = cacheHits;
174
- const totalMisses = cacheMisses;
175
- const hitRate = totalHits + totalMisses > 0
176
- ? (totalHits / (totalHits + totalMisses)) * 100
177
- : 0;
178
- return {
179
- totalEntries,
180
- hitCount: totalHits,
181
- missCount: totalMisses,
182
- hitRate: Math.round(hitRate * 100) / 100,
183
- byType,
184
- };
185
- }
186
- catch (error) {
187
- logger.warn('[AI Cache] Error getting stats', { error });
188
- return {
189
- totalEntries: 0,
190
- hitCount: cacheHits,
191
- missCount: cacheMisses,
192
- hitRate: 0,
193
- byType: {},
194
- };
195
- }
196
- }
197
- /**
198
- * Reset in-memory stats (for testing)
199
- */
200
- export function resetCacheStats() {
201
- cacheHits = 0;
202
- cacheMisses = 0;
203
- }
204
- //# sourceMappingURL=ai-cache.js.map