@symerian/symi 3.0.18 → 3.0.19
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/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/package.json +1 -1
- package/extensions/copilot-proxy/README.md +0 -24
- package/extensions/copilot-proxy/index.ts +0 -154
- package/extensions/copilot-proxy/node_modules/.bin/symi +0 -21
- package/extensions/copilot-proxy/package.json +0 -15
- package/extensions/copilot-proxy/symi.plugin.json +0 -9
- package/extensions/device-pair/index.ts +0 -642
- package/extensions/device-pair/symi.plugin.json +0 -20
- package/extensions/diagnostics-otel/index.ts +0 -15
- package/extensions/diagnostics-otel/node_modules/.bin/acorn +0 -21
- package/extensions/diagnostics-otel/node_modules/.bin/symi +0 -21
- package/extensions/diagnostics-otel/package.json +0 -27
- package/extensions/diagnostics-otel/src/service.test.ts +0 -290
- package/extensions/diagnostics-otel/src/service.ts +0 -666
- package/extensions/diagnostics-otel/symi.plugin.json +0 -8
- package/extensions/google-antigravity-auth/README.md +0 -24
- package/extensions/google-antigravity-auth/index.ts +0 -424
- package/extensions/google-antigravity-auth/node_modules/.bin/symi +0 -21
- package/extensions/google-antigravity-auth/package.json +0 -15
- package/extensions/google-antigravity-auth/symi.plugin.json +0 -9
- package/extensions/google-gemini-cli-auth/README.md +0 -35
- package/extensions/google-gemini-cli-auth/index.ts +0 -75
- package/extensions/google-gemini-cli-auth/node_modules/.bin/symi +0 -21
- package/extensions/google-gemini-cli-auth/oauth.test.ts +0 -162
- package/extensions/google-gemini-cli-auth/oauth.ts +0 -636
- package/extensions/google-gemini-cli-auth/package.json +0 -15
- package/extensions/google-gemini-cli-auth/symi.plugin.json +0 -9
- package/extensions/learning-loop/index.ts +0 -159
- package/extensions/learning-loop/node_modules/.bin/symi +0 -21
- package/extensions/learning-loop/package.json +0 -18
- package/extensions/learning-loop/src/analytics/gateway-methods.ts +0 -230
- package/extensions/learning-loop/src/analytics/metrics-aggregator.ts +0 -153
- package/extensions/learning-loop/src/capture/run-tracker.ts +0 -181
- package/extensions/learning-loop/src/capture/serializer.ts +0 -74
- package/extensions/learning-loop/src/db.ts +0 -583
- package/extensions/learning-loop/src/feedback/explicit-feedback.ts +0 -58
- package/extensions/learning-loop/src/feedback/implicit-signals.ts +0 -89
- package/extensions/learning-loop/src/graph/edge-inference.ts +0 -189
- package/extensions/learning-loop/src/graph/graph-retrieval.ts +0 -144
- package/extensions/learning-loop/src/graph/graph-store.ts +0 -183
- package/extensions/learning-loop/src/hooks.ts +0 -244
- package/extensions/learning-loop/src/injection/cache.ts +0 -73
- package/extensions/learning-loop/src/injection/context-injector.ts +0 -104
- package/extensions/learning-loop/src/injection/prompt-builder.ts +0 -43
- package/extensions/learning-loop/src/learning/embedding-bridge.ts +0 -54
- package/extensions/learning-loop/src/learning/learning-extractor.ts +0 -217
- package/extensions/learning-loop/src/learning/learning-store.ts +0 -158
- package/extensions/learning-loop/src/learning/retrieval.ts +0 -87
- package/extensions/learning-loop/src/math/confidence-intervals.ts +0 -62
- package/extensions/learning-loop/src/math/ewma.ts +0 -51
- package/extensions/learning-loop/src/math/weighted-scorer.ts +0 -42
- package/extensions/learning-loop/src/schema.ts +0 -176
- package/extensions/learning-loop/src/scoring/normalization.ts +0 -32
- package/extensions/learning-loop/src/scoring/quality-engine.ts +0 -78
- package/extensions/learning-loop/src/scoring/signal-extractors.ts +0 -155
- package/extensions/learning-loop/src/test/context-injector.test.ts +0 -142
- package/extensions/learning-loop/src/test/fixes.test.ts +0 -1286
- package/extensions/learning-loop/src/test/graph.test.ts +0 -711
- package/extensions/learning-loop/src/test/integration.test.ts +0 -312
- package/extensions/learning-loop/src/test/learning-store.test.ts +0 -191
- package/extensions/learning-loop/src/test/math.test.ts +0 -148
- package/extensions/learning-loop/src/test/quality-engine.test.ts +0 -231
- package/extensions/learning-loop/src/test/run-tracker.test.ts +0 -143
- package/extensions/learning-loop/src/types.ts +0 -281
- package/extensions/learning-loop/symi.plugin.json +0 -46
- package/extensions/llm-task/README.md +0 -97
- package/extensions/llm-task/index.ts +0 -6
- package/extensions/llm-task/package.json +0 -12
- package/extensions/llm-task/src/llm-task-tool.test.ts +0 -138
- package/extensions/llm-task/src/llm-task-tool.ts +0 -249
- package/extensions/llm-task/symi.plugin.json +0 -21
- package/extensions/memory-lancedb/config.ts +0 -161
- package/extensions/memory-lancedb/index.test.ts +0 -330
- package/extensions/memory-lancedb/index.ts +0 -670
- package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +0 -21
- package/extensions/memory-lancedb/node_modules/.bin/openai +0 -21
- package/extensions/memory-lancedb/node_modules/.bin/symi +0 -21
- package/extensions/memory-lancedb/package.json +0 -20
- package/extensions/memory-lancedb/symi.plugin.json +0 -71
- package/extensions/minimax-portal-auth/README.md +0 -33
- package/extensions/minimax-portal-auth/index.ts +0 -161
- package/extensions/minimax-portal-auth/node_modules/.bin/symi +0 -21
- package/extensions/minimax-portal-auth/oauth.ts +0 -247
- package/extensions/minimax-portal-auth/package.json +0 -15
- package/extensions/minimax-portal-auth/symi.plugin.json +0 -9
- package/extensions/model-equalizer/index.ts +0 -80
- package/extensions/model-equalizer/skills/model-equalizer/SKILL.md +0 -58
- package/extensions/model-equalizer/src/detection.ts +0 -62
- package/extensions/model-equalizer/src/enhancer.ts +0 -63
- package/extensions/model-equalizer/src/test/detection.test.ts +0 -218
- package/extensions/model-equalizer/src/test/enhancer.test.ts +0 -137
- package/extensions/model-equalizer/src/test/integration.test.ts +0 -185
- package/extensions/model-equalizer/src/types.ts +0 -24
- package/extensions/model-equalizer/symi.plugin.json +0 -12
- package/extensions/phone-control/index.ts +0 -421
- package/extensions/phone-control/symi.plugin.json +0 -10
- package/extensions/pipeline/README.md +0 -75
- package/extensions/pipeline/SKILL.md +0 -97
- package/extensions/pipeline/index.ts +0 -18
- package/extensions/pipeline/package.json +0 -11
- package/extensions/pipeline/src/pipeline-tool.test.ts +0 -345
- package/extensions/pipeline/src/pipeline-tool.ts +0 -266
- package/extensions/pipeline/src/windows-spawn.test.ts +0 -148
- package/extensions/pipeline/src/windows-spawn.ts +0 -193
- package/extensions/pipeline/symi.plugin.json +0 -10
- package/extensions/qwen-portal-auth/README.md +0 -24
- package/extensions/qwen-portal-auth/index.ts +0 -134
- package/extensions/qwen-portal-auth/oauth.ts +0 -190
- package/extensions/qwen-portal-auth/symi.plugin.json +0 -9
- package/extensions/talk-voice/index.ts +0 -150
- package/extensions/talk-voice/symi.plugin.json +0 -10
- package/extensions/thread-ownership/index.test.ts +0 -180
- package/extensions/thread-ownership/index.ts +0 -133
- package/extensions/thread-ownership/symi.plugin.json +0 -28
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { resolveMemorySearchConfig } from "../../src/agents/memory-search.js";
|
|
3
|
-
import { createEmbeddingProvider } from "../../src/memory/embeddings.js";
|
|
4
|
-
import type { SymiPluginApi } from "../../src/plugins/types.js";
|
|
5
|
-
import { registerGatewayMethods } from "./src/analytics/gateway-methods.js";
|
|
6
|
-
import { createMetricsAggregator } from "./src/analytics/metrics-aggregator.js";
|
|
7
|
-
import { createRunTracker } from "./src/capture/run-tracker.js";
|
|
8
|
-
import { createDatabaseManager } from "./src/db.js";
|
|
9
|
-
import { createImplicitSignals } from "./src/feedback/implicit-signals.js";
|
|
10
|
-
import { createEdgeInference } from "./src/graph/edge-inference.js";
|
|
11
|
-
import { createGraphRetrieval } from "./src/graph/graph-retrieval.js";
|
|
12
|
-
import { createGraphStore } from "./src/graph/graph-store.js";
|
|
13
|
-
import { registerHooks } from "./src/hooks.js";
|
|
14
|
-
import { createContextInjector } from "./src/injection/context-injector.js";
|
|
15
|
-
import { createEmbeddingBridge, type EmbeddingBridge } from "./src/learning/embedding-bridge.js";
|
|
16
|
-
import { createLearningExtractor } from "./src/learning/learning-extractor.js";
|
|
17
|
-
import { createLearningStore } from "./src/learning/learning-store.js";
|
|
18
|
-
import { createQualityEngine } from "./src/scoring/quality-engine.js";
|
|
19
|
-
import { resolveConfig } from "./src/types.js";
|
|
20
|
-
|
|
21
|
-
const plugin = {
|
|
22
|
-
id: "learning-loop",
|
|
23
|
-
name: "Learning Loop",
|
|
24
|
-
description:
|
|
25
|
-
"Closed-loop learning with deterministic quality scoring, pattern extraction, and context injection",
|
|
26
|
-
|
|
27
|
-
register(api: SymiPluginApi) {
|
|
28
|
-
const config = resolveConfig(api.pluginConfig);
|
|
29
|
-
const logger = api.logger;
|
|
30
|
-
const rootStateDir = api.runtime.state.resolveStateDir();
|
|
31
|
-
const stateDir = path.join(rootStateDir, "learning-loop");
|
|
32
|
-
|
|
33
|
-
const db = createDatabaseManager({ stateDir, config, logger });
|
|
34
|
-
const tracker = createRunTracker();
|
|
35
|
-
const qualityEngine = createQualityEngine({ config, db });
|
|
36
|
-
const graphStore = createGraphStore({ db });
|
|
37
|
-
const learningStore = createLearningStore({ db, graphStore });
|
|
38
|
-
const edgeInference = createEdgeInference({ graphStore, learningStore });
|
|
39
|
-
const graphRetrieval = createGraphRetrieval({ graphStore, learningStore, config });
|
|
40
|
-
|
|
41
|
-
// Lazy embedding bridge: initializes on first use, avoids blocking sync register()
|
|
42
|
-
let bridgePromise: Promise<EmbeddingBridge> | null = null;
|
|
43
|
-
let bridgeResolved = false;
|
|
44
|
-
let bridgeHasProvider = false;
|
|
45
|
-
|
|
46
|
-
async function initBridge(): Promise<EmbeddingBridge> {
|
|
47
|
-
try {
|
|
48
|
-
const memCfg = resolveMemorySearchConfig(api.config, "main");
|
|
49
|
-
const result = await createEmbeddingProvider({
|
|
50
|
-
config: api.config,
|
|
51
|
-
provider: memCfg?.provider ?? "auto",
|
|
52
|
-
model: memCfg?.model ?? "",
|
|
53
|
-
fallback: memCfg?.fallback ?? "none",
|
|
54
|
-
local: memCfg?.local,
|
|
55
|
-
remote: memCfg?.remote,
|
|
56
|
-
});
|
|
57
|
-
return createEmbeddingBridge({ provider: result.provider, logger });
|
|
58
|
-
} catch (err) {
|
|
59
|
-
logger.warn(`[learning-loop] embedding bridge init failed: ${String(err)}`);
|
|
60
|
-
return createEmbeddingBridge({ provider: null, logger });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function initAndTrack(): Promise<EmbeddingBridge> {
|
|
65
|
-
const bridge = await initBridge();
|
|
66
|
-
bridgeResolved = true;
|
|
67
|
-
bridgeHasProvider = bridge.available();
|
|
68
|
-
return bridge;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const lazyBridge: EmbeddingBridge = {
|
|
72
|
-
async embed(text) {
|
|
73
|
-
if (!bridgePromise) bridgePromise = initAndTrack();
|
|
74
|
-
return (await bridgePromise).embed(text);
|
|
75
|
-
},
|
|
76
|
-
async embedBatch(texts) {
|
|
77
|
-
if (!bridgePromise) bridgePromise = initAndTrack();
|
|
78
|
-
return (await bridgePromise).embedBatch(texts);
|
|
79
|
-
},
|
|
80
|
-
available: () => (bridgeResolved ? bridgeHasProvider : true),
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const extractor = createLearningExtractor({
|
|
84
|
-
db,
|
|
85
|
-
learningStore,
|
|
86
|
-
config,
|
|
87
|
-
edgeInference,
|
|
88
|
-
embeddingBridge: lazyBridge,
|
|
89
|
-
});
|
|
90
|
-
const injector = createContextInjector({
|
|
91
|
-
db,
|
|
92
|
-
learningStore,
|
|
93
|
-
config,
|
|
94
|
-
embeddingBridge: lazyBridge,
|
|
95
|
-
graphRetrieval,
|
|
96
|
-
});
|
|
97
|
-
const metrics = createMetricsAggregator({ db });
|
|
98
|
-
const implicit = createImplicitSignals({ db, config });
|
|
99
|
-
|
|
100
|
-
registerHooks(api, {
|
|
101
|
-
config,
|
|
102
|
-
logger,
|
|
103
|
-
db,
|
|
104
|
-
tracker,
|
|
105
|
-
qualityEngine,
|
|
106
|
-
learningStore,
|
|
107
|
-
extractor,
|
|
108
|
-
injector,
|
|
109
|
-
metrics,
|
|
110
|
-
implicit,
|
|
111
|
-
edgeInference,
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
registerGatewayMethods(api, {
|
|
115
|
-
db,
|
|
116
|
-
learningStore,
|
|
117
|
-
metrics,
|
|
118
|
-
injector,
|
|
119
|
-
qualityEngine,
|
|
120
|
-
graphStore,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// One-time async backfill: embed historical learnings that have no embeddings
|
|
124
|
-
async function backfillEmbeddings(): Promise<void> {
|
|
125
|
-
const BATCH_SIZE = 50;
|
|
126
|
-
let totalBackfilled = 0;
|
|
127
|
-
|
|
128
|
-
// eslint-disable-next-line no-constant-condition
|
|
129
|
-
while (true) {
|
|
130
|
-
const rows = db.getLearningsWithoutEmbeddings(BATCH_SIZE);
|
|
131
|
-
if (rows.length === 0) break;
|
|
132
|
-
|
|
133
|
-
const texts = rows.map((r) => r.content);
|
|
134
|
-
const embeddings = await lazyBridge.embedBatch(texts);
|
|
135
|
-
|
|
136
|
-
for (let i = 0; i < rows.length; i++) {
|
|
137
|
-
const emb = embeddings[i];
|
|
138
|
-
if (emb) {
|
|
139
|
-
db.updateLearningEmbedding(rows[i]!.id, emb);
|
|
140
|
-
totalBackfilled++;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (rows.length < BATCH_SIZE) break;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (totalBackfilled > 0) {
|
|
148
|
-
logger.debug?.(`[learning-loop] backfilled embeddings for ${totalBackfilled} learnings`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Fire-and-forget: backfill after bridge is ready
|
|
153
|
-
backfillEmbeddings().catch((err) => {
|
|
154
|
-
logger.warn(`[learning-loop] embedding backfill failed: ${String(err)}`);
|
|
155
|
-
});
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
export default plugin;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
-
|
|
4
|
-
case `uname` in
|
|
5
|
-
*CYGWIN*|*MINGW*|*MSYS*)
|
|
6
|
-
if command -v cygpath > /dev/null 2>&1; then
|
|
7
|
-
basedir=`cygpath -w "$basedir"`
|
|
8
|
-
fi
|
|
9
|
-
;;
|
|
10
|
-
esac
|
|
11
|
-
|
|
12
|
-
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/home/symi/projects/symi/node_modules:/home/symi/projects/node_modules:/home/symi/node_modules:/home/node_modules:/node_modules:/home/symi/projects/symi/node_modules/.pnpm/node_modules"
|
|
14
|
-
else
|
|
15
|
-
export NODE_PATH="/home/symi/projects/symi/node_modules:/home/symi/projects/node_modules:/home/symi/node_modules:/home/node_modules:/node_modules:/home/symi/projects/symi/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
|
-
fi
|
|
17
|
-
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir/../@symerian/symi/symi.mjs" "$@"
|
|
19
|
-
else
|
|
20
|
-
exec node "$basedir/../@symerian/symi/symi.mjs" "$@"
|
|
21
|
-
fi
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@symi/learning-loop",
|
|
3
|
-
"version": "3.0.9",
|
|
4
|
-
"private": true,
|
|
5
|
-
"description": "Closed-loop learning extension with deterministic quality scoring and vector-indexed learning storage",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"devDependencies": {
|
|
8
|
-
"@symerian/symi": "workspace:*"
|
|
9
|
-
},
|
|
10
|
-
"peerDependencies": {
|
|
11
|
-
"@symerian/symi": ">=2026.1.26"
|
|
12
|
-
},
|
|
13
|
-
"symi": {
|
|
14
|
-
"extensions": [
|
|
15
|
-
"./index.ts"
|
|
16
|
-
]
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gateway RPC methods for the Glass UI.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { SymiPluginApi } from "../../../../src/plugins/types.js";
|
|
6
|
-
import type { DatabaseManager } from "../db.js";
|
|
7
|
-
import { createExplicitFeedbackHandler } from "../feedback/explicit-feedback.js";
|
|
8
|
-
import type { GraphStore } from "../graph/graph-store.js";
|
|
9
|
-
import type { ContextInjector } from "../injection/context-injector.js";
|
|
10
|
-
import type { LearningStore } from "../learning/learning-store.js";
|
|
11
|
-
import type { QualityEngine } from "../scoring/quality-engine.js";
|
|
12
|
-
import type { LearningCategory, EdgeType } from "../types.js";
|
|
13
|
-
import type { MetricsAggregator } from "./metrics-aggregator.js";
|
|
14
|
-
|
|
15
|
-
export function registerGatewayMethods(
|
|
16
|
-
api: SymiPluginApi,
|
|
17
|
-
deps: {
|
|
18
|
-
db: DatabaseManager;
|
|
19
|
-
learningStore: LearningStore;
|
|
20
|
-
metrics: MetricsAggregator;
|
|
21
|
-
injector: ContextInjector;
|
|
22
|
-
qualityEngine?: QualityEngine;
|
|
23
|
-
graphStore?: GraphStore;
|
|
24
|
-
},
|
|
25
|
-
) {
|
|
26
|
-
const { db, learningStore, metrics, injector, qualityEngine, graphStore } = deps;
|
|
27
|
-
const feedbackHandler = createExplicitFeedbackHandler({ db, qualityEngine });
|
|
28
|
-
|
|
29
|
-
// learning.metrics.models
|
|
30
|
-
api.registerGatewayMethod("learning.metrics.models", (opts) => {
|
|
31
|
-
try {
|
|
32
|
-
const params = opts.params as { provider?: string; model?: string };
|
|
33
|
-
const result = metrics.getModelMetrics(params.provider, params.model);
|
|
34
|
-
opts.respond(true, result);
|
|
35
|
-
} catch (err) {
|
|
36
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// learning.metrics.timeseries
|
|
41
|
-
api.registerGatewayMethod("learning.metrics.timeseries", (opts) => {
|
|
42
|
-
try {
|
|
43
|
-
const params = opts.params as {
|
|
44
|
-
provider?: string;
|
|
45
|
-
model?: string;
|
|
46
|
-
fromHour?: string;
|
|
47
|
-
toHour?: string;
|
|
48
|
-
};
|
|
49
|
-
const result = metrics.getTimeseries(params);
|
|
50
|
-
opts.respond(true, result);
|
|
51
|
-
} catch (err) {
|
|
52
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// learning.metrics.leaderboard
|
|
57
|
-
api.registerGatewayMethod("learning.metrics.leaderboard", (opts) => {
|
|
58
|
-
try {
|
|
59
|
-
const result = metrics.getLeaderboard();
|
|
60
|
-
opts.respond(true, result);
|
|
61
|
-
} catch (err) {
|
|
62
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// learning.learnings.list
|
|
67
|
-
api.registerGatewayMethod("learning.learnings.list", (opts) => {
|
|
68
|
-
try {
|
|
69
|
-
const params = opts.params as { category?: string; limit?: number };
|
|
70
|
-
const result = learningStore.listLearnings({
|
|
71
|
-
category: params.category as LearningCategory | undefined,
|
|
72
|
-
limit: params.limit,
|
|
73
|
-
});
|
|
74
|
-
opts.respond(true, result);
|
|
75
|
-
} catch (err) {
|
|
76
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// learning.learnings.search
|
|
81
|
-
api.registerGatewayMethod("learning.learnings.search", (opts) => {
|
|
82
|
-
try {
|
|
83
|
-
const params = opts.params as { query: string; limit?: number };
|
|
84
|
-
if (!params.query) {
|
|
85
|
-
opts.respond(false, undefined, { code: 400, message: "query is required" });
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const result = learningStore.searchByText(params.query, params.limit ?? 20);
|
|
89
|
-
opts.respond(true, result);
|
|
90
|
-
} catch (err) {
|
|
91
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// learning.feedback.submit
|
|
96
|
-
api.registerGatewayMethod("learning.feedback.submit", (opts) => {
|
|
97
|
-
try {
|
|
98
|
-
const params = opts.params as { runId: string; score: number };
|
|
99
|
-
if (!params.runId || params.score == null) {
|
|
100
|
-
opts.respond(false, undefined, {
|
|
101
|
-
code: 400,
|
|
102
|
-
message: "runId and score (1-5) are required",
|
|
103
|
-
});
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const result = feedbackHandler.submitFeedback(params.runId, params.score);
|
|
107
|
-
if (!result) {
|
|
108
|
-
opts.respond(false, undefined, { code: 404, message: "Run not found" });
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
opts.respond(true, result);
|
|
112
|
-
} catch (err) {
|
|
113
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// learning.status
|
|
118
|
-
api.registerGatewayMethod("learning.status", (opts) => {
|
|
119
|
-
try {
|
|
120
|
-
const stats = db.getStats();
|
|
121
|
-
const cacheStats = injector.getCacheStats();
|
|
122
|
-
opts.respond(true, {
|
|
123
|
-
...stats,
|
|
124
|
-
cache: cacheStats,
|
|
125
|
-
});
|
|
126
|
-
} catch (err) {
|
|
127
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// --- Graph methods ---
|
|
132
|
-
|
|
133
|
-
if (graphStore) {
|
|
134
|
-
// learning.graph.edges
|
|
135
|
-
api.registerGatewayMethod("learning.graph.edges", (opts) => {
|
|
136
|
-
try {
|
|
137
|
-
const params = opts.params as { learningId: string; typeFilter?: EdgeType[] };
|
|
138
|
-
if (!params.learningId) {
|
|
139
|
-
opts.respond(false, undefined, { code: 400, message: "learningId is required" });
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const edges = graphStore.getEdges(params.learningId, {
|
|
143
|
-
typeFilter: params.typeFilter,
|
|
144
|
-
});
|
|
145
|
-
opts.respond(true, edges);
|
|
146
|
-
} catch (err) {
|
|
147
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// learning.graph.neighbors
|
|
152
|
-
api.registerGatewayMethod("learning.graph.neighbors", (opts) => {
|
|
153
|
-
try {
|
|
154
|
-
const params = opts.params as {
|
|
155
|
-
learningId: string;
|
|
156
|
-
hops?: number;
|
|
157
|
-
typeFilter?: EdgeType[];
|
|
158
|
-
};
|
|
159
|
-
if (!params.learningId) {
|
|
160
|
-
opts.respond(false, undefined, { code: 400, message: "learningId is required" });
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const neighbors = graphStore.getNeighbors(
|
|
164
|
-
params.learningId,
|
|
165
|
-
params.hops ?? 1,
|
|
166
|
-
params.typeFilter,
|
|
167
|
-
);
|
|
168
|
-
// Convert Map to serializable object
|
|
169
|
-
const result: Array<{ id: string; weight: number }> = [];
|
|
170
|
-
for (const [id, weight] of neighbors) {
|
|
171
|
-
result.push({ id, weight });
|
|
172
|
-
}
|
|
173
|
-
opts.respond(true, result);
|
|
174
|
-
} catch (err) {
|
|
175
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
// learning.graph.contradictions
|
|
180
|
-
api.registerGatewayMethod("learning.graph.contradictions", (opts) => {
|
|
181
|
-
try {
|
|
182
|
-
const params = opts.params as { learningId?: string } | undefined;
|
|
183
|
-
const contradictions = graphStore.getContradictions(params?.learningId);
|
|
184
|
-
opts.respond(true, contradictions);
|
|
185
|
-
} catch (err) {
|
|
186
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// learning.graph.stats
|
|
191
|
-
api.registerGatewayMethod("learning.graph.stats", (opts) => {
|
|
192
|
-
try {
|
|
193
|
-
const edgeTypes: EdgeType[] = ["T", "S", "C", "U", "X", "R"];
|
|
194
|
-
const countsByType: Record<string, number> = {};
|
|
195
|
-
let totalEdges = 0;
|
|
196
|
-
for (const t of edgeTypes) {
|
|
197
|
-
const edges = db.getEdgesByType(t);
|
|
198
|
-
countsByType[t] = edges.length;
|
|
199
|
-
totalEdges += edges.length;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const learnings = db.getAllLearnings(10000);
|
|
203
|
-
let centralitySum = 0;
|
|
204
|
-
let centralityCount = 0;
|
|
205
|
-
for (const l of learnings) {
|
|
206
|
-
const score = db.getCentralityScore(l.id);
|
|
207
|
-
if (score > 0) {
|
|
208
|
-
centralitySum += score;
|
|
209
|
-
centralityCount++;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const avgCentrality = centralityCount > 0 ? centralitySum / centralityCount : 0;
|
|
214
|
-
const nodeCount = learnings.length;
|
|
215
|
-
const maxEdges = nodeCount > 1 ? (nodeCount * (nodeCount - 1)) / 2 : 0;
|
|
216
|
-
const density = maxEdges > 0 ? totalEdges / maxEdges : 0;
|
|
217
|
-
|
|
218
|
-
opts.respond(true, {
|
|
219
|
-
totalEdges,
|
|
220
|
-
countsByType,
|
|
221
|
-
avgCentrality,
|
|
222
|
-
nodeCount,
|
|
223
|
-
density,
|
|
224
|
-
});
|
|
225
|
-
} catch (err) {
|
|
226
|
-
opts.respond(false, undefined, { code: 500, message: String(err) });
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pre-aggregated time bucket metrics.
|
|
3
|
-
*
|
|
4
|
-
* Uses O(1) upsert per run via SQLite's ON CONFLICT UPDATE.
|
|
5
|
-
* Variance computed via: var = sum_sq/n - (sum/n)^2
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { DatabaseManager } from "../db.js";
|
|
9
|
-
import { wilsonScoreLowerBound } from "../math/confidence-intervals.js";
|
|
10
|
-
import type { CompletedRun, QualityScore, MetricsBucketRow } from "../types.js";
|
|
11
|
-
|
|
12
|
-
export type MetricsAggregator = ReturnType<typeof createMetricsAggregator>;
|
|
13
|
-
|
|
14
|
-
export function createMetricsAggregator(params: { db: DatabaseManager }) {
|
|
15
|
-
const { db } = params;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Record a completed run in the metrics buckets. O(1) upsert.
|
|
19
|
-
*/
|
|
20
|
-
function recordRun(run: CompletedRun, score: QualityScore): void {
|
|
21
|
-
db.upsertMetricsBucket(
|
|
22
|
-
run.provider,
|
|
23
|
-
run.model,
|
|
24
|
-
run.completedAt,
|
|
25
|
-
score.score,
|
|
26
|
-
run.durationMs,
|
|
27
|
-
run.success,
|
|
28
|
-
run.usage.input ?? 0,
|
|
29
|
-
run.usage.output ?? 0,
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Get per-model quality, success rate, latency, and token efficiency.
|
|
35
|
-
*/
|
|
36
|
-
function getModelMetrics(
|
|
37
|
-
provider?: string,
|
|
38
|
-
model?: string,
|
|
39
|
-
): Array<{
|
|
40
|
-
provider: string;
|
|
41
|
-
model: string;
|
|
42
|
-
runCount: number;
|
|
43
|
-
successRate: number;
|
|
44
|
-
successRateLowerBound: number;
|
|
45
|
-
avgQuality: number;
|
|
46
|
-
qualityVariance: number;
|
|
47
|
-
avgLatency: number;
|
|
48
|
-
latencyVariance: number;
|
|
49
|
-
avgTokenInput: number;
|
|
50
|
-
avgTokenOutput: number;
|
|
51
|
-
}> {
|
|
52
|
-
const buckets = db.getMetricsBuckets(provider, model);
|
|
53
|
-
const grouped = new Map<string, MetricsBucketRow[]>();
|
|
54
|
-
|
|
55
|
-
for (const b of buckets) {
|
|
56
|
-
const key = `${b.provider}:${b.model}`;
|
|
57
|
-
const group = grouped.get(key) ?? [];
|
|
58
|
-
group.push(b);
|
|
59
|
-
grouped.set(key, group);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const results: Array<{
|
|
63
|
-
provider: string;
|
|
64
|
-
model: string;
|
|
65
|
-
runCount: number;
|
|
66
|
-
successRate: number;
|
|
67
|
-
successRateLowerBound: number;
|
|
68
|
-
avgQuality: number;
|
|
69
|
-
qualityVariance: number;
|
|
70
|
-
avgLatency: number;
|
|
71
|
-
latencyVariance: number;
|
|
72
|
-
avgTokenInput: number;
|
|
73
|
-
avgTokenOutput: number;
|
|
74
|
-
}> = [];
|
|
75
|
-
|
|
76
|
-
for (const [, group] of grouped) {
|
|
77
|
-
const totalRuns = group.reduce((s, b) => s + b.run_count, 0);
|
|
78
|
-
if (totalRuns === 0) continue;
|
|
79
|
-
|
|
80
|
-
const totalSuccess = group.reduce((s, b) => s + b.success_count, 0);
|
|
81
|
-
const qualitySum = group.reduce((s, b) => s + b.quality_sum, 0);
|
|
82
|
-
const qualitySumSq = group.reduce((s, b) => s + b.quality_sum_sq, 0);
|
|
83
|
-
const latencySum = group.reduce((s, b) => s + b.latency_sum, 0);
|
|
84
|
-
const latencySumSq = group.reduce((s, b) => s + b.latency_sum_sq, 0);
|
|
85
|
-
const tokenInputSum = group.reduce((s, b) => s + b.token_input_sum, 0);
|
|
86
|
-
const tokenOutputSum = group.reduce((s, b) => s + b.token_output_sum, 0);
|
|
87
|
-
|
|
88
|
-
const avgQuality = qualitySum / totalRuns;
|
|
89
|
-
const avgLatency = latencySum / totalRuns;
|
|
90
|
-
const successRate = totalSuccess / totalRuns;
|
|
91
|
-
|
|
92
|
-
results.push({
|
|
93
|
-
provider: group[0]!.provider,
|
|
94
|
-
model: group[0]!.model,
|
|
95
|
-
runCount: totalRuns,
|
|
96
|
-
successRate,
|
|
97
|
-
successRateLowerBound: wilsonScoreLowerBound(totalSuccess, totalRuns),
|
|
98
|
-
avgQuality,
|
|
99
|
-
qualityVariance: Math.max(0, qualitySumSq / totalRuns - avgQuality ** 2),
|
|
100
|
-
avgLatency,
|
|
101
|
-
latencyVariance: Math.max(0, latencySumSq / totalRuns - avgLatency ** 2),
|
|
102
|
-
avgTokenInput: tokenInputSum / totalRuns,
|
|
103
|
-
avgTokenOutput: tokenOutputSum / totalRuns,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return results.sort((a, b) => b.avgQuality - a.avgQuality);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get time-bucketed quality trends.
|
|
112
|
-
*/
|
|
113
|
-
function getTimeseries(params?: {
|
|
114
|
-
provider?: string;
|
|
115
|
-
model?: string;
|
|
116
|
-
fromHour?: string;
|
|
117
|
-
toHour?: string;
|
|
118
|
-
}): Array<{
|
|
119
|
-
bucketHour: string;
|
|
120
|
-
provider: string;
|
|
121
|
-
model: string;
|
|
122
|
-
runCount: number;
|
|
123
|
-
avgQuality: number;
|
|
124
|
-
successRate: number;
|
|
125
|
-
avgLatency: number;
|
|
126
|
-
}> {
|
|
127
|
-
const buckets = db.getMetricsBuckets(
|
|
128
|
-
params?.provider,
|
|
129
|
-
params?.model,
|
|
130
|
-
params?.fromHour,
|
|
131
|
-
params?.toHour,
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
return buckets.map((b) => ({
|
|
135
|
-
bucketHour: b.bucket_hour,
|
|
136
|
-
provider: b.provider,
|
|
137
|
-
model: b.model,
|
|
138
|
-
runCount: b.run_count,
|
|
139
|
-
avgQuality: b.run_count > 0 ? b.quality_sum / b.run_count : 0,
|
|
140
|
-
successRate: b.run_count > 0 ? b.success_count / b.run_count : 0,
|
|
141
|
-
avgLatency: b.run_count > 0 ? b.latency_sum / b.run_count : 0,
|
|
142
|
-
}));
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Get model comparison leaderboard.
|
|
147
|
-
*/
|
|
148
|
-
function getLeaderboard() {
|
|
149
|
-
return db.getModelLeaderboard();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return { recordRun, getModelMetrics, getTimeseries, getLeaderboard };
|
|
153
|
-
}
|