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.
- package/CHANGELOG.md +21 -38
- package/CONTRIBUTING.md +3 -5
- package/README.md +128 -63
- package/dist/chat/execution/tools/index.d.ts +1 -1
- package/dist/chat/execution/tools/index.js +41 -2
- package/dist/chat/providers/slack/index.d.ts +2 -2
- package/dist/cli/commands/models.js +39 -26
- package/dist/cli/commands/onboard.js +64 -79
- package/dist/cli/commands/setup.js +293 -90
- package/dist/cli/index.js +0 -6
- package/dist/cli/providers.d.ts +15 -0
- package/dist/cli/providers.js +41 -0
- package/dist/core/deployment.js +2 -0
- package/dist/providers/ai-sdk.js +26 -6
- package/dist/routes/channels.d.ts +12 -0
- package/dist/routes/channels.js +166 -0
- package/dist/server/route-loader.js +1 -0
- package/dist/server.js +115 -109
- package/package.json +1 -1
- package/skills/git-workflow/SKILL.md +3 -3
- package/ui/dist/assets/{ActivityView-DIPQSJ_d.js → ActivityView-BeshZXmE.js} +1 -1
- package/ui/dist/assets/{AgentList-CqyaYrcq.js → AgentList-QVHeldPi.js} +1 -1
- package/ui/dist/assets/{AnalyticsDashboard-C-mjuGHT.js → AnalyticsDashboard-C9R97dXE.js} +1 -1
- package/ui/dist/assets/{AreaChart-CfYWmbtr.js → AreaChart-B73gaiVQ.js} +1 -1
- package/ui/dist/assets/{CartesianChart-BaQ3It0c.js → CartesianChart-CXYL2R-A.js} +1 -1
- package/ui/dist/assets/{ChatView-DxxQy6pl.js → ChatView-qjZErlrp.js} +1 -1
- package/ui/dist/assets/{CostsDashboard-CDBBcGkg.js → CostsDashboard-O4DCLy1i.js} +1 -1
- package/ui/dist/assets/{EstimateSelect-CDjf5qKl.js → EstimateSelect-1hQf4Osk.js} +1 -1
- package/ui/dist/assets/{FloatingChatbot-CZHjvJIe.js → FloatingChatbot-BZBzrJxd.js} +1 -1
- package/ui/dist/assets/{InviteCodeManagement-DTm-IPTm.js → InviteCodeManagement-LEoGqYeA.js} +1 -1
- package/ui/dist/assets/{ProjectDetail-DlYUcOss.js → ProjectDetail--gPfvATB.js} +1 -1
- package/ui/dist/assets/{ProjectIcon-BMRpnM04.js → ProjectIcon-Chd_ofsw.js} +1 -1
- package/ui/dist/assets/{ProjectList-D85TN0i0.js → ProjectList-BvUfVB6c.js} +1 -1
- package/ui/dist/assets/{Settings-CgPqzUvj.js → Settings-1kaf7VHE.js} +1 -1
- package/ui/dist/assets/{StatusIndicator-yig0uZEA.js → StatusIndicator-pRfkNcdV.js} +1 -1
- package/ui/dist/assets/{SummaryDetail-CwLAfHgJ.js → SummaryDetail-DV3AmONk.js} +1 -1
- package/ui/dist/assets/{SummaryList-BvS5Idnu.js → SummaryList-B6kXiTxL.js} +1 -1
- package/ui/dist/assets/{TaskDetail-zarC1fOt.js → TaskDetail-CV6CzbOS.js} +1 -1
- package/ui/dist/assets/{TaskList-CyJq3ANE.js → TaskList-DL7brQKd.js} +1 -1
- package/ui/dist/assets/{TicketBoard-BlWr3-iH.js → TicketBoard-CrkkFj7C.js} +1 -1
- package/ui/dist/assets/{TicketDetail-DmqWKj2W.js → TicketDetail-hnnYclpD.js} +1 -1
- package/ui/dist/assets/{TicketList-Scj4pMWa.js → TicketList-CsDAyQCL.js} +1 -1
- package/ui/dist/assets/{UserManagement-BH7uG7dw.js → UserManagement-BGFQyTSr.js} +1 -1
- package/ui/dist/assets/{ViewSwitcher-3HzZUdWn.js → ViewSwitcher-CnL0OqA8.js} +1 -1
- package/ui/dist/assets/{alert-dialog-DWPb2IfQ.js → alert-dialog-SKM6Gjz4.js} +1 -1
- package/ui/dist/assets/{archive-CGck7gxR.js → archive-BsLa7brF.js} +1 -1
- package/ui/dist/assets/{badge-DEmc3Z_l.js → badge-CVHH1a-v.js} +1 -1
- package/ui/dist/assets/{brain-CfFdLVqb.js → brain-6o_RHVpe.js} +1 -1
- package/ui/dist/assets/{bug-DqSGPUUE.js → bug-CmZI7jqM.js} +1 -1
- package/ui/dist/assets/{calendar-BuZ4zbPW.js → calendar-Bf0t2B82.js} +1 -1
- package/ui/dist/assets/{chevron-up-DcJ4LL_N.js → chevron-up-CoUsOvZj.js} +1 -1
- package/ui/dist/assets/{circle-x-C78PNZjF.js → circle-x-Dgw11LgT.js} +1 -1
- package/ui/dist/assets/{constants-DhDjZe4P.js → constants-Q5Zivl-a.js} +1 -1
- package/ui/dist/assets/{cpu-Ck6AoG8c.js → cpu-DiPMQICV.js} +1 -1
- package/ui/dist/assets/{database-CA2w8Lvt.js → database-BwTfupDi.js} +1 -1
- package/ui/dist/assets/{dialog-BOdGWhgE.js → dialog-BUqN9Psj.js} +1 -1
- package/ui/dist/assets/{ellipsis-FhHKRbfl.js → ellipsis-UJx4Xnf6.js} +1 -1
- package/ui/dist/assets/{ellipsis-vertical-CO0UqE4-.js → ellipsis-vertical-DoULYEKn.js} +1 -1
- package/ui/dist/assets/{file-code-ChnhYYj1.js → file-code-B2UrJLwi.js} +1 -1
- package/ui/dist/assets/{folder-open-CHwA1-ve.js → folder-open-BJi577JH.js} +1 -1
- package/ui/dist/assets/{funnel-ByEjRFNB.js → funnel-PkaD_HSR.js} +1 -1
- package/ui/dist/assets/{git-branch-GTOPQgU4.js → git-branch-C_K30am0.js} +1 -1
- package/ui/dist/assets/{git-commit-horizontal-F5M84Kut.js → git-commit-horizontal-IT6ecxWQ.js} +1 -1
- package/ui/dist/assets/{globe-CQWfr7hT.js → globe-f-jjCa_g.js} +1 -1
- package/ui/dist/assets/{hash-nKC9gtLg.js → hash-DNEv8Elo.js} +1 -1
- package/ui/dist/assets/{history-DfFSA9xJ.js → history-BQSBo_1Q.js} +1 -1
- package/ui/dist/assets/{index-DHGvP9mp.js → index-BUmnwPQr.js} +1 -1
- package/ui/dist/assets/{index-Z4-RtZcs.js → index-BfF5G4Cw.js} +1 -1
- package/ui/dist/assets/{index-DeI8lKRR.js → index-C8N375c2.js} +1 -1
- package/ui/dist/assets/index-CuxnqVVz.css +1 -0
- package/ui/dist/assets/{index-Ca3vk7Gq.js → index-CzMM3QOk.js} +1 -1
- package/ui/dist/assets/{index--JkIxVXb.js → index-lJlNPFVJ.js} +3 -3
- package/ui/dist/assets/{index-a4Fxq0Hb.js → index-nS3EErDg.js} +1 -1
- package/ui/dist/assets/{layers-DFeLExSa.js → layers-jFqVrWMD.js} +1 -1
- package/ui/dist/assets/{layout-dashboard-WmgjMhWF.js → layout-dashboard-CcKt1hXw.js} +1 -1
- package/ui/dist/assets/{lightbulb-Bo8hEr7B.js → lightbulb-Jhp7FvbU.js} +1 -1
- package/ui/dist/assets/{link-2-YqrpQXBH.js → link-2-DdO7ostF.js} +1 -1
- package/ui/dist/assets/{markdown-renderer-Croje6Xe.js → markdown-renderer-n1ToC4XQ.js} +1 -1
- package/ui/dist/assets/{message-square-CD0H5hot.js → message-square-CBgH65X7.js} +1 -1
- package/ui/dist/assets/{pencil-DpFnj5dn.js → pencil-CYXCEPQh.js} +1 -1
- package/ui/dist/assets/{play-DVgea_jT.js → play-d1ONaH0-.js} +1 -1
- package/ui/dist/assets/{popover-JXecfWG5.js → popover-CrmZgoWu.js} +1 -1
- package/ui/dist/assets/{progress-C1ghf1Fu.js → progress-uv4SPkL5.js} +1 -1
- package/ui/dist/assets/{rocket-BFxPYGx9.js → rocket-DhvUetsk.js} +1 -1
- package/ui/dist/assets/{select-MADP8m6Q.js → select-CeQ8AHGy.js} +1 -1
- package/ui/dist/assets/{send-eYnW_7n-.js → send-P0r5D7td.js} +1 -1
- package/ui/dist/assets/{server-CPMaysH8.js → server-CVHTZB72.js} +1 -1
- package/ui/dist/assets/{settings-2-scXUOuE_.js → settings-2-ocj5uHGx.js} +1 -1
- package/ui/dist/assets/{sheet-r6NSOxi-.js → sheet-DuQExSho.js} +1 -1
- package/ui/dist/assets/{shield-check-PEr7nmVw.js → shield-check-DfVdE34l.js} +1 -1
- package/ui/dist/assets/{shield-off-CLtXio62.js → shield-off-8l0J6R-8.js} +1 -1
- package/ui/dist/assets/{skeleton-C9oEzNlU.js → skeleton-TFiFJ3nB.js} +1 -1
- package/ui/dist/assets/{smartphone-DusFfY0U.js → smartphone-77_g3SQn.js} +1 -1
- package/ui/dist/assets/{square-e1yxB2eR.js → square-Ckzsr3Rn.js} +1 -1
- package/ui/dist/assets/{switch-tNs0n-kz.js → switch-CAePkd6k.js} +1 -1
- package/ui/dist/assets/{table-CXxtLicg.js → table-ZjAZ7hkt.js} +1 -1
- package/ui/dist/assets/{tag-tAwMmQ9G.js → tag-C35mgoEv.js} +1 -1
- package/ui/dist/assets/{textarea-BjiEeIWh.js → textarea-DsVPnSgR.js} +1 -1
- package/ui/dist/assets/{timer-Vel1N3RP.js → timer-DHJBS2w1.js} +1 -1
- package/ui/dist/assets/{tooltip-ZMwtfr1J.js → tooltip-BYmQ331W.js} +1 -1
- package/ui/dist/assets/{trash-2-eM-CNwsZ.js → trash-2-keO5tKPE.js} +1 -1
- package/ui/dist/assets/{trending-up-BGafmjRo.js → trending-up-BBv8KbrY.js} +1 -1
- package/ui/dist/assets/{wand-sparkles-DPvcd9Pi.js → wand-sparkles-BusQ52ZK.js} +1 -1
- package/ui/dist/assets/{wrench-B8s3zYVV.js → wrench-02YaOSiK.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/dist/chat/auto-reply.d.ts +0 -90
- package/dist/chat/auto-reply.js +0 -272
- package/dist/chat/execution/pty.d.ts +0 -92
- package/dist/chat/execution/pty.js +0 -285
- package/dist/chat/execution/tools/glinr-ops.d.ts +0 -202
- package/dist/chat/execution/tools/glinr-ops.js +0 -694
- package/dist/cli/commands/browser.d.ts +0 -3
- package/dist/cli/commands/browser.js +0 -138
- package/dist/cli/commands/canvas.d.ts +0 -3
- package/dist/cli/commands/canvas.js +0 -155
- package/dist/cli/commands/nodes.d.ts +0 -3
- package/dist/cli/commands/nodes.js +0 -193
- package/dist/diagnostics/exporters.d.ts +0 -21
- package/dist/diagnostics/exporters.js +0 -138
- package/dist/diagnostics/index.d.ts +0 -10
- package/dist/diagnostics/index.js +0 -9
- package/dist/diagnostics/middleware.d.ts +0 -12
- package/dist/diagnostics/middleware.js +0 -58
- package/dist/diagnostics/tracer.d.ts +0 -133
- package/dist/diagnostics/tracer.js +0 -257
- package/dist/discovery/index.d.ts +0 -8
- package/dist/discovery/index.js +0 -7
- package/dist/discovery/mdns.d.ts +0 -40
- package/dist/discovery/mdns.js +0 -249
- package/dist/plugins/registry.d.ts +0 -122
- package/dist/plugins/registry.js +0 -352
- package/dist/plugins/sandbox.d.ts +0 -77
- package/dist/plugins/sandbox.js +0 -287
- package/dist/plugins/search/brave.d.ts +0 -9
- package/dist/plugins/search/brave.js +0 -157
- package/dist/plugins/search/duckduckgo.d.ts +0 -9
- package/dist/plugins/search/duckduckgo.js +0 -171
- package/dist/plugins/search/index.d.ts +0 -13
- package/dist/plugins/search/index.js +0 -23
- package/dist/plugins/search/searxng.d.ts +0 -9
- package/dist/plugins/search/searxng.js +0 -170
- package/dist/plugins/search/serper.d.ts +0 -9
- package/dist/plugins/search/serper.js +0 -135
- package/dist/plugins/search/tavily.d.ts +0 -9
- package/dist/plugins/search/tavily.js +0 -146
- package/dist/providers/adapters/anthropic.d.ts +0 -26
- package/dist/providers/adapters/anthropic.js +0 -306
- package/dist/providers/adapters/base.d.ts +0 -53
- package/dist/providers/adapters/base.js +0 -99
- package/dist/providers/adapters/ollama.d.ts +0 -30
- package/dist/providers/adapters/ollama.js +0 -292
- package/dist/utils/ai-cache.d.ts +0 -72
- package/dist/utils/ai-cache.js +0 -204
- package/dist/utils/file-lock.d.ts +0 -85
- package/dist/utils/file-lock.js +0 -204
- 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
|
package/dist/utils/ai-cache.d.ts
DELETED
|
@@ -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
|
package/dist/utils/ai-cache.js
DELETED
|
@@ -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
|