@xandout/libra-harness 0.1.8
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/LICENSE +21 -0
- package/README.md +486 -0
- package/dist/agent.d.ts +174 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +575 -0
- package/dist/agent.js.map +1 -0
- package/dist/ai-sdk-model.d.ts +26 -0
- package/dist/ai-sdk-model.d.ts.map +1 -0
- package/dist/ai-sdk-model.js +260 -0
- package/dist/ai-sdk-model.js.map +1 -0
- package/dist/context.d.ts +75 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/extension.d.ts +63 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +2 -0
- package/dist/extension.js.map +1 -0
- package/dist/extras/extension-loader.d.ts +205 -0
- package/dist/extras/extension-loader.d.ts.map +1 -0
- package/dist/extras/extension-loader.js +315 -0
- package/dist/extras/extension-loader.js.map +1 -0
- package/dist/extras/extensions/auto-steer/extension.d.ts +46 -0
- package/dist/extras/extensions/auto-steer/extension.d.ts.map +1 -0
- package/dist/extras/extensions/auto-steer/extension.js +52 -0
- package/dist/extras/extensions/auto-steer/extension.js.map +1 -0
- package/dist/extras/extensions/auto-steer/extension.json +7 -0
- package/dist/extras/extensions/auto-steer/index.d.ts +3 -0
- package/dist/extras/extensions/auto-steer/index.d.ts.map +1 -0
- package/dist/extras/extensions/auto-steer/index.js +2 -0
- package/dist/extras/extensions/auto-steer/index.js.map +1 -0
- package/dist/extras/extensions/disk-session/extension.d.ts +167 -0
- package/dist/extras/extensions/disk-session/extension.d.ts.map +1 -0
- package/dist/extras/extensions/disk-session/extension.js +492 -0
- package/dist/extras/extensions/disk-session/extension.js.map +1 -0
- package/dist/extras/extensions/disk-session/extension.json +6 -0
- package/dist/extras/extensions/disk-session/extension.test.d.ts +2 -0
- package/dist/extras/extensions/disk-session/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/disk-session/extension.test.js +672 -0
- package/dist/extras/extensions/disk-session/extension.test.js.map +1 -0
- package/dist/extras/extensions/disk-session/index.d.ts +3 -0
- package/dist/extras/extensions/disk-session/index.d.ts.map +1 -0
- package/dist/extras/extensions/disk-session/index.js +2 -0
- package/dist/extras/extensions/disk-session/index.js.map +1 -0
- package/dist/extras/extensions/emoji/extension.d.ts +20 -0
- package/dist/extras/extensions/emoji/extension.d.ts.map +1 -0
- package/dist/extras/extensions/emoji/extension.js +22 -0
- package/dist/extras/extensions/emoji/extension.js.map +1 -0
- package/dist/extras/extensions/emoji/extension.json +7 -0
- package/dist/extras/extensions/emoji/index.d.ts +3 -0
- package/dist/extras/extensions/emoji/index.d.ts.map +1 -0
- package/dist/extras/extensions/emoji/index.js +2 -0
- package/dist/extras/extensions/emoji/index.js.map +1 -0
- package/dist/extras/extensions/filesystem/extension.d.ts +53 -0
- package/dist/extras/extensions/filesystem/extension.d.ts.map +1 -0
- package/dist/extras/extensions/filesystem/extension.js +350 -0
- package/dist/extras/extensions/filesystem/extension.js.map +1 -0
- package/dist/extras/extensions/filesystem/extension.json +6 -0
- package/dist/extras/extensions/filesystem/extension.test.d.ts +2 -0
- package/dist/extras/extensions/filesystem/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/filesystem/extension.test.js +387 -0
- package/dist/extras/extensions/filesystem/extension.test.js.map +1 -0
- package/dist/extras/extensions/filesystem/index.d.ts +2 -0
- package/dist/extras/extensions/filesystem/index.d.ts.map +1 -0
- package/dist/extras/extensions/filesystem/index.js +2 -0
- package/dist/extras/extensions/filesystem/index.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/extension.d.ts +53 -0
- package/dist/extras/extensions/keyword-extractor/extension.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/extension.js +61 -0
- package/dist/extras/extensions/keyword-extractor/extension.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/extension.json +6 -0
- package/dist/extras/extensions/keyword-extractor/extension.test.d.ts +2 -0
- package/dist/extras/extensions/keyword-extractor/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/extension.test.js +105 -0
- package/dist/extras/extensions/keyword-extractor/extension.test.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/index.d.ts +5 -0
- package/dist/extras/extensions/keyword-extractor/index.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/index.js +3 -0
- package/dist/extras/extensions/keyword-extractor/index.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/nlp.d.ts +68 -0
- package/dist/extras/extensions/keyword-extractor/nlp.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/nlp.js +156 -0
- package/dist/extras/extensions/keyword-extractor/nlp.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/nlp.test.d.ts +2 -0
- package/dist/extras/extensions/keyword-extractor/nlp.test.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/nlp.test.js +176 -0
- package/dist/extras/extensions/keyword-extractor/nlp.test.js.map +1 -0
- package/dist/extras/extensions/logger/extension.d.ts +52 -0
- package/dist/extras/extensions/logger/extension.d.ts.map +1 -0
- package/dist/extras/extensions/logger/extension.js +68 -0
- package/dist/extras/extensions/logger/extension.js.map +1 -0
- package/dist/extras/extensions/logger/extension.json +7 -0
- package/dist/extras/extensions/logger/index.d.ts +3 -0
- package/dist/extras/extensions/logger/index.d.ts.map +1 -0
- package/dist/extras/extensions/logger/index.js +2 -0
- package/dist/extras/extensions/logger/index.js.map +1 -0
- package/dist/extras/extensions/mcp/extension.d.ts +63 -0
- package/dist/extras/extensions/mcp/extension.d.ts.map +1 -0
- package/dist/extras/extensions/mcp/extension.js +437 -0
- package/dist/extras/extensions/mcp/extension.js.map +1 -0
- package/dist/extras/extensions/mcp/extension.json +7 -0
- package/dist/extras/extensions/mcp/extension.test.d.ts +2 -0
- package/dist/extras/extensions/mcp/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/mcp/extension.test.js +286 -0
- package/dist/extras/extensions/mcp/extension.test.js.map +1 -0
- package/dist/extras/extensions/mcp/index.d.ts +3 -0
- package/dist/extras/extensions/mcp/index.d.ts.map +1 -0
- package/dist/extras/extensions/mcp/index.js +2 -0
- package/dist/extras/extensions/mcp/index.js.map +1 -0
- package/dist/extras/extensions/mem-session/extension.d.ts +24 -0
- package/dist/extras/extensions/mem-session/extension.d.ts.map +1 -0
- package/dist/extras/extensions/mem-session/extension.js +49 -0
- package/dist/extras/extensions/mem-session/extension.js.map +1 -0
- package/dist/extras/extensions/mem-session/extension.json +6 -0
- package/dist/extras/extensions/mem-session/index.d.ts +2 -0
- package/dist/extras/extensions/mem-session/index.d.ts.map +1 -0
- package/dist/extras/extensions/mem-session/index.js +2 -0
- package/dist/extras/extensions/mem-session/index.js.map +1 -0
- package/dist/extras/extensions/memory/extension.d.ts +54 -0
- package/dist/extras/extensions/memory/extension.d.ts.map +1 -0
- package/dist/extras/extensions/memory/extension.js +143 -0
- package/dist/extras/extensions/memory/extension.js.map +1 -0
- package/dist/extras/extensions/memory/extension.json +7 -0
- package/dist/extras/extensions/memory/index.d.ts +5 -0
- package/dist/extras/extensions/memory/index.d.ts.map +1 -0
- package/dist/extras/extensions/memory/index.js +3 -0
- package/dist/extras/extensions/memory/index.js.map +1 -0
- package/dist/extras/extensions/memory/llm-extractor.d.ts +42 -0
- package/dist/extras/extensions/memory/llm-extractor.d.ts.map +1 -0
- package/dist/extras/extensions/memory/llm-extractor.js +89 -0
- package/dist/extras/extensions/memory/llm-extractor.js.map +1 -0
- package/dist/extras/extensions/memory/types.d.ts +142 -0
- package/dist/extras/extensions/memory/types.d.ts.map +1 -0
- package/dist/extras/extensions/memory/types.js +2 -0
- package/dist/extras/extensions/memory/types.js.map +1 -0
- package/dist/extras/extensions/otel/extension.d.ts +57 -0
- package/dist/extras/extensions/otel/extension.d.ts.map +1 -0
- package/dist/extras/extensions/otel/extension.js +253 -0
- package/dist/extras/extensions/otel/extension.js.map +1 -0
- package/dist/extras/extensions/otel/extension.json +7 -0
- package/dist/extras/extensions/otel/extension.test.d.ts +2 -0
- package/dist/extras/extensions/otel/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/otel/extension.test.js +399 -0
- package/dist/extras/extensions/otel/extension.test.js.map +1 -0
- package/dist/extras/extensions/otel/index.d.ts +4 -0
- package/dist/extras/extensions/otel/index.d.ts.map +1 -0
- package/dist/extras/extensions/otel/index.js +3 -0
- package/dist/extras/extensions/otel/index.js.map +1 -0
- package/dist/extras/extensions/otel/jsonl-span-exporter.d.ts +60 -0
- package/dist/extras/extensions/otel/jsonl-span-exporter.d.ts.map +1 -0
- package/dist/extras/extensions/otel/jsonl-span-exporter.js +134 -0
- package/dist/extras/extensions/otel/jsonl-span-exporter.js.map +1 -0
- package/dist/extras/extensions/scripts/extension.d.ts +180 -0
- package/dist/extras/extensions/scripts/extension.d.ts.map +1 -0
- package/dist/extras/extensions/scripts/extension.js +618 -0
- package/dist/extras/extensions/scripts/extension.js.map +1 -0
- package/dist/extras/extensions/scripts/extension.json +7 -0
- package/dist/extras/extensions/scripts/extension.test.d.ts +2 -0
- package/dist/extras/extensions/scripts/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/scripts/extension.test.js +552 -0
- package/dist/extras/extensions/scripts/extension.test.js.map +1 -0
- package/dist/extras/extensions/scripts/index.d.ts +3 -0
- package/dist/extras/extensions/scripts/index.d.ts.map +1 -0
- package/dist/extras/extensions/scripts/index.js +2 -0
- package/dist/extras/extensions/scripts/index.js.map +1 -0
- package/dist/extras/extensions/skills/extension.d.ts +105 -0
- package/dist/extras/extensions/skills/extension.d.ts.map +1 -0
- package/dist/extras/extensions/skills/extension.js +419 -0
- package/dist/extras/extensions/skills/extension.js.map +1 -0
- package/dist/extras/extensions/skills/extension.json +7 -0
- package/dist/extras/extensions/skills/extension.test.d.ts +2 -0
- package/dist/extras/extensions/skills/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/skills/extension.test.js +184 -0
- package/dist/extras/extensions/skills/extension.test.js.map +1 -0
- package/dist/extras/extensions/skills/index.d.ts +4 -0
- package/dist/extras/extensions/skills/index.d.ts.map +1 -0
- package/dist/extras/extensions/skills/index.js +3 -0
- package/dist/extras/extensions/skills/index.js.map +1 -0
- package/dist/extras/extensions/streaming/extension.d.ts +15 -0
- package/dist/extras/extensions/streaming/extension.d.ts.map +1 -0
- package/dist/extras/extensions/streaming/extension.js +41 -0
- package/dist/extras/extensions/streaming/extension.js.map +1 -0
- package/dist/extras/extensions/streaming/extension.json +6 -0
- package/dist/extras/extensions/streaming/index.d.ts +2 -0
- package/dist/extras/extensions/streaming/index.d.ts.map +1 -0
- package/dist/extras/extensions/streaming/index.js +2 -0
- package/dist/extras/extensions/streaming/index.js.map +1 -0
- package/dist/extras/extensions/structured-output/extension.d.ts +11 -0
- package/dist/extras/extensions/structured-output/extension.d.ts.map +1 -0
- package/dist/extras/extensions/structured-output/extension.js +102 -0
- package/dist/extras/extensions/structured-output/extension.js.map +1 -0
- package/dist/extras/extensions/structured-output/extension.json +7 -0
- package/dist/extras/extensions/structured-output/index.d.ts +3 -0
- package/dist/extras/extensions/structured-output/index.d.ts.map +1 -0
- package/dist/extras/extensions/structured-output/index.js +2 -0
- package/dist/extras/extensions/structured-output/index.js.map +1 -0
- package/dist/extras/extensions/timestamp/extension.d.ts +11 -0
- package/dist/extras/extensions/timestamp/extension.d.ts.map +1 -0
- package/dist/extras/extensions/timestamp/extension.js +21 -0
- package/dist/extras/extensions/timestamp/extension.js.map +1 -0
- package/dist/extras/extensions/timestamp/extension.json +6 -0
- package/dist/extras/extensions/timestamp/index.d.ts +2 -0
- package/dist/extras/extensions/timestamp/index.d.ts.map +1 -0
- package/dist/extras/extensions/timestamp/index.js +2 -0
- package/dist/extras/extensions/timestamp/index.js.map +1 -0
- package/dist/extras/extensions/token-stats/extension.d.ts +50 -0
- package/dist/extras/extensions/token-stats/extension.d.ts.map +1 -0
- package/dist/extras/extensions/token-stats/extension.js +99 -0
- package/dist/extras/extensions/token-stats/extension.js.map +1 -0
- package/dist/extras/extensions/token-stats/extension.json +7 -0
- package/dist/extras/extensions/token-stats/extension.test.d.ts +2 -0
- package/dist/extras/extensions/token-stats/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/token-stats/extension.test.js +197 -0
- package/dist/extras/extensions/token-stats/extension.test.js.map +1 -0
- package/dist/extras/extensions/token-stats/index.d.ts +3 -0
- package/dist/extras/extensions/token-stats/index.d.ts.map +1 -0
- package/dist/extras/extensions/token-stats/index.js +2 -0
- package/dist/extras/extensions/token-stats/index.js.map +1 -0
- package/dist/extras/extensions/tool-buffer/extension.d.ts +89 -0
- package/dist/extras/extensions/tool-buffer/extension.d.ts.map +1 -0
- package/dist/extras/extensions/tool-buffer/extension.js +182 -0
- package/dist/extras/extensions/tool-buffer/extension.js.map +1 -0
- package/dist/extras/extensions/tool-buffer/extension.json +7 -0
- package/dist/extras/extensions/tool-buffer/extension.test.d.ts +2 -0
- package/dist/extras/extensions/tool-buffer/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/tool-buffer/extension.test.js +549 -0
- package/dist/extras/extensions/tool-buffer/extension.test.js.map +1 -0
- package/dist/extras/extensions/tool-buffer/index.d.ts +3 -0
- package/dist/extras/extensions/tool-buffer/index.d.ts.map +1 -0
- package/dist/extras/extensions/tool-buffer/index.js +2 -0
- package/dist/extras/extensions/tool-buffer/index.js.map +1 -0
- package/dist/extras/extensions/weather-tool/extension.d.ts +22 -0
- package/dist/extras/extensions/weather-tool/extension.d.ts.map +1 -0
- package/dist/extras/extensions/weather-tool/extension.js +33 -0
- package/dist/extras/extensions/weather-tool/extension.js.map +1 -0
- package/dist/extras/extensions/weather-tool/extension.json +7 -0
- package/dist/extras/extensions/weather-tool/index.d.ts +3 -0
- package/dist/extras/extensions/weather-tool/index.d.ts.map +1 -0
- package/dist/extras/extensions/weather-tool/index.js +2 -0
- package/dist/extras/extensions/weather-tool/index.js.map +1 -0
- package/dist/extras/models/ai-sdk-resolver.d.ts +15 -0
- package/dist/extras/models/ai-sdk-resolver.d.ts.map +1 -0
- package/dist/extras/models/ai-sdk-resolver.js +57 -0
- package/dist/extras/models/ai-sdk-resolver.js.map +1 -0
- package/dist/extras/models/index.d.ts +5 -0
- package/dist/extras/models/index.d.ts.map +1 -0
- package/dist/extras/models/index.js +3 -0
- package/dist/extras/models/index.js.map +1 -0
- package/dist/extras/models/routing-model.d.ts +18 -0
- package/dist/extras/models/routing-model.d.ts.map +1 -0
- package/dist/extras/models/routing-model.js +24 -0
- package/dist/extras/models/routing-model.js.map +1 -0
- package/dist/extras/openai-provider/index.d.ts +3 -0
- package/dist/extras/openai-provider/index.d.ts.map +1 -0
- package/dist/extras/openai-provider/index.js +2 -0
- package/dist/extras/openai-provider/index.js.map +1 -0
- package/dist/extras/openai-provider/server.d.ts +34 -0
- package/dist/extras/openai-provider/server.d.ts.map +1 -0
- package/dist/extras/openai-provider/server.js +335 -0
- package/dist/extras/openai-provider/server.js.map +1 -0
- package/dist/handle.d.ts +56 -0
- package/dist/handle.d.ts.map +1 -0
- package/dist/handle.js +2 -0
- package/dist/handle.js.map +1 -0
- package/dist/hooks.d.ts +98 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +53 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/model.d.ts +75 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +2 -0
- package/dist/model.js.map +1 -0
- package/dist/tool.d.ts +78 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +78 -0
- package/dist/tool.js.map +1 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +178 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { Agent } from '@xandout/libra-harness';
|
|
3
|
+
import { createWinkQueryAnalyzer } from './index.js';
|
|
4
|
+
import { createKeywordExtractorExtension } from './index.js';
|
|
5
|
+
// Mock model — returns a fixed assistant reply without touching a provider.
|
|
6
|
+
function mockModel() {
|
|
7
|
+
return {
|
|
8
|
+
async generate() {
|
|
9
|
+
return {
|
|
10
|
+
message: { role: 'assistant', content: 'ok' },
|
|
11
|
+
finishReason: 'stop',
|
|
12
|
+
usage: { promptTokens: 0, completionTokens: 0 },
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
describe('keyword-extractor extension', () => {
|
|
18
|
+
it('extracts terms and writes sessionMeta.keywords on beforeTurn', async () => {
|
|
19
|
+
const extracted = [];
|
|
20
|
+
let capturedMeta;
|
|
21
|
+
const analyzer = createWinkQueryAnalyzer();
|
|
22
|
+
const ext = createKeywordExtractorExtension({
|
|
23
|
+
analyzer,
|
|
24
|
+
log: (entry) => extracted.push(entry),
|
|
25
|
+
});
|
|
26
|
+
const agent = new Agent({ model: mockModel() });
|
|
27
|
+
// Register the extension first so its beforeTurn hook runs before
|
|
28
|
+
// the observer (hooks run in registration order within a stage).
|
|
29
|
+
agent.use(ext);
|
|
30
|
+
agent.hook('beforeTurn', 'observer', async (ctx) => {
|
|
31
|
+
capturedMeta = ctx.turn.metadata.sessionMeta;
|
|
32
|
+
});
|
|
33
|
+
await agent.run({
|
|
34
|
+
message: 'Can you find our documentation about the MCP server architecture?',
|
|
35
|
+
});
|
|
36
|
+
expect(extracted).toHaveLength(1);
|
|
37
|
+
expect(extracted[0].message).toBe('Can you find our documentation about the MCP server architecture?');
|
|
38
|
+
expect(extracted[0].terms).toEqual([
|
|
39
|
+
'find',
|
|
40
|
+
'documentation',
|
|
41
|
+
'MCP',
|
|
42
|
+
'server',
|
|
43
|
+
'architecture',
|
|
44
|
+
]);
|
|
45
|
+
expect(extracted[0].phrases).toContain('MCP server architecture');
|
|
46
|
+
// The enrichment bag should contain keywords under the right shape.
|
|
47
|
+
expect(capturedMeta).toBeDefined();
|
|
48
|
+
expect(capturedMeta.keywords).toEqual({
|
|
49
|
+
terms: ['find', 'documentation', 'MCP', 'server', 'architecture'],
|
|
50
|
+
phrases: expect.arrayContaining(['MCP server architecture']),
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
it('extracts empty terms for an empty message', async () => {
|
|
54
|
+
const extracted = [];
|
|
55
|
+
const ext = createKeywordExtractorExtension({
|
|
56
|
+
analyzer: createWinkQueryAnalyzer(),
|
|
57
|
+
log: (entry) => extracted.push(entry),
|
|
58
|
+
});
|
|
59
|
+
const agent = new Agent({ model: mockModel() });
|
|
60
|
+
agent.use(ext);
|
|
61
|
+
await agent.run({ message: '' });
|
|
62
|
+
expect(extracted).toHaveLength(1);
|
|
63
|
+
expect(extracted[0].terms).toEqual([]);
|
|
64
|
+
expect(extracted[0].phrases).toEqual([]);
|
|
65
|
+
});
|
|
66
|
+
it('uses the shared analyzer when none is provided', async () => {
|
|
67
|
+
const extracted = [];
|
|
68
|
+
const ext = createKeywordExtractorExtension({ log: (e) => extracted.push(e) });
|
|
69
|
+
const agent = new Agent({ model: mockModel() });
|
|
70
|
+
agent.use(ext);
|
|
71
|
+
await agent.run({ message: 'Where did we store agent memory?' });
|
|
72
|
+
expect(extracted[0].terms).toEqual(['store', 'agent', 'memory']);
|
|
73
|
+
});
|
|
74
|
+
it('merges into existing sessionMeta without overwriting other keys', async () => {
|
|
75
|
+
let capturedMeta;
|
|
76
|
+
const ext = createKeywordExtractorExtension({
|
|
77
|
+
analyzer: createWinkQueryAnalyzer(),
|
|
78
|
+
log: () => { },
|
|
79
|
+
});
|
|
80
|
+
const agent = new Agent({ model: mockModel() });
|
|
81
|
+
// A prior extension with higher priority writes its own key first.
|
|
82
|
+
const priorExt = {
|
|
83
|
+
name: 'prior',
|
|
84
|
+
priority: 100,
|
|
85
|
+
install(a) {
|
|
86
|
+
a.hook('beforeTurn', 'prior', async (ctx) => {
|
|
87
|
+
ctx.turn.metadata.sessionMeta = { sentiment: 'positive' };
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
agent.use(priorExt);
|
|
92
|
+
// keyword-extractor (priority 50) runs second and should merge, not overwrite.
|
|
93
|
+
agent.use(ext);
|
|
94
|
+
// Observe after both have run (default priority 0 = last).
|
|
95
|
+
agent.hook('beforeTurn', 'observer', async (ctx) => {
|
|
96
|
+
capturedMeta = ctx.turn.metadata.sessionMeta;
|
|
97
|
+
});
|
|
98
|
+
await agent.run({ message: 'Find the MCP documentation' });
|
|
99
|
+
expect(capturedMeta).toBeDefined();
|
|
100
|
+
expect(capturedMeta.sentiment).toBe('positive');
|
|
101
|
+
expect(capturedMeta.keywords).toBeDefined();
|
|
102
|
+
expect(capturedMeta.keywords.terms).toContain('MCP');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=extension.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.test.js","sourceRoot":"","sources":["../../../../src/extras/extensions/keyword-extractor/extension.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as createKeywordExtractorExtension } from './extension.js';
|
|
2
|
+
export type { KeywordExtractorConfig, KeywordExtractionEntry } from './extension.js';
|
|
3
|
+
export type { SearchTerms, QueryAnalyzer } from './nlp.js';
|
|
4
|
+
export { WinkQueryAnalyzer, getQueryAnalyzer, createWinkQueryAnalyzer } from './nlp.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/keyword-extractor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,+BAA+B,EAAE,MAAM,gBAAgB,CAAC;AAC5E,YAAY,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACrF,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/extras/extensions/keyword-extractor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,+BAA+B,EAAE,MAAM,gBAAgB,CAAC;AAG5E,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The result of analyzing a user message for search terms.
|
|
3
|
+
*/
|
|
4
|
+
export interface SearchTerms {
|
|
5
|
+
/**
|
|
6
|
+
* Ordered, de-duplicated single-word search terms extracted from the
|
|
7
|
+
* input. Conversational filler, punctuation, and stop words are
|
|
8
|
+
* removed. Capitalization is preserved for proper nouns / technical
|
|
9
|
+
* terms (e.g. `TypeScript`, `AWS`, `MCP`); other terms are lowercased.
|
|
10
|
+
*/
|
|
11
|
+
terms: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Multi-word phrases (n-grams of length 2–3) built from consecutive
|
|
14
|
+
* retained terms that were adjacent in the original text. Longer
|
|
15
|
+
* phrases appear first. May be empty or undefined when no multi-word
|
|
16
|
+
* phrases were found.
|
|
17
|
+
*/
|
|
18
|
+
phrases?: string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Analyzes conversational text and returns search terms.
|
|
22
|
+
*
|
|
23
|
+
* Implementations must be deterministic and safe to reuse across many
|
|
24
|
+
* turns of a long-running agent process (the NLP model should be
|
|
25
|
+
* initialized once, not per call).
|
|
26
|
+
*/
|
|
27
|
+
export interface QueryAnalyzer {
|
|
28
|
+
analyze(input: string): SearchTerms;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* winkNLP-backed implementation of {@link QueryAnalyzer}.
|
|
32
|
+
*
|
|
33
|
+
* The winkNLP model is initialized exactly once (lazily on first use,
|
|
34
|
+
* then reused). This is safe for a long-running agent process —
|
|
35
|
+
* `analyze()` never re-instantiates the NLP engine.
|
|
36
|
+
*
|
|
37
|
+
* Extraction steps:
|
|
38
|
+
* 1. Tokenize the input.
|
|
39
|
+
* 2. Drop punctuation and non-word tokens.
|
|
40
|
+
* 3. Drop English stop words.
|
|
41
|
+
* 4. Keep tokens whose POS is a noun, proper noun, verb, or adjective.
|
|
42
|
+
* 5. Preserve the original surface form for proper nouns (so
|
|
43
|
+
* `TypeScript`, `AWS`, `MCP`, `Kubernetes` keep their casing);
|
|
44
|
+
* lowercase everything else via winkNLP's `normal` form.
|
|
45
|
+
* 6. De-duplicate while preserving first-occurrence order.
|
|
46
|
+
* 7. Build 2–3 word phrases from consecutive retained tokens that were
|
|
47
|
+
* adjacent in the original text.
|
|
48
|
+
*/
|
|
49
|
+
export declare class WinkQueryAnalyzer implements QueryAnalyzer {
|
|
50
|
+
private nlp;
|
|
51
|
+
/** Lazily create the winkNLP engine exactly once. */
|
|
52
|
+
private engine;
|
|
53
|
+
analyze(input: string): SearchTerms;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Returns a process-wide shared {@link WinkQueryAnalyzer} instance.
|
|
57
|
+
* The NLP model is initialized lazily on first use and reused for all
|
|
58
|
+
* subsequent calls.
|
|
59
|
+
*/
|
|
60
|
+
export declare function getQueryAnalyzer(): QueryAnalyzer;
|
|
61
|
+
/**
|
|
62
|
+
* Creates a fresh, independent {@link WinkQueryAnalyzer} instance.
|
|
63
|
+
*
|
|
64
|
+
* Prefer {@link getQueryAnalyzer} unless you specifically need an
|
|
65
|
+
* isolated instance (e.g. in tests that want to control initialization).
|
|
66
|
+
*/
|
|
67
|
+
export declare function createWinkQueryAnalyzer(): QueryAnalyzer;
|
|
68
|
+
//# sourceMappingURL=nlp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nlp.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/keyword-extractor/nlp.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;CACrC;AA4BD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,OAAO,CAAC,GAAG,CAAyC;IAEpD,qDAAqD;IACrD,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAyClC;CACF;AAqDD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAGhD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,aAAa,CAEvD"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import winkNLP from 'wink-nlp';
|
|
2
|
+
import model from 'wink-eng-lite-web-model';
|
|
3
|
+
/**
|
|
4
|
+
* Part-of-speech tags worth retaining as search terms.
|
|
5
|
+
*
|
|
6
|
+
* Prioritizes nouns, proper nouns, and meaningful verbs/adjectives.
|
|
7
|
+
* Stop words, punctuation, pronouns, determiners, conjunctions, etc.
|
|
8
|
+
* are dropped (either via the stop-word flag or by not being in this
|
|
9
|
+
* set).
|
|
10
|
+
*/
|
|
11
|
+
const RETAINED_POS = new Set([
|
|
12
|
+
'NOUN',
|
|
13
|
+
'PROPN',
|
|
14
|
+
'VERB',
|
|
15
|
+
'ADJ',
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* winkNLP-backed implementation of {@link QueryAnalyzer}.
|
|
19
|
+
*
|
|
20
|
+
* The winkNLP model is initialized exactly once (lazily on first use,
|
|
21
|
+
* then reused). This is safe for a long-running agent process —
|
|
22
|
+
* `analyze()` never re-instantiates the NLP engine.
|
|
23
|
+
*
|
|
24
|
+
* Extraction steps:
|
|
25
|
+
* 1. Tokenize the input.
|
|
26
|
+
* 2. Drop punctuation and non-word tokens.
|
|
27
|
+
* 3. Drop English stop words.
|
|
28
|
+
* 4. Keep tokens whose POS is a noun, proper noun, verb, or adjective.
|
|
29
|
+
* 5. Preserve the original surface form for proper nouns (so
|
|
30
|
+
* `TypeScript`, `AWS`, `MCP`, `Kubernetes` keep their casing);
|
|
31
|
+
* lowercase everything else via winkNLP's `normal` form.
|
|
32
|
+
* 6. De-duplicate while preserving first-occurrence order.
|
|
33
|
+
* 7. Build 2–3 word phrases from consecutive retained tokens that were
|
|
34
|
+
* adjacent in the original text.
|
|
35
|
+
*/
|
|
36
|
+
export class WinkQueryAnalyzer {
|
|
37
|
+
nlp;
|
|
38
|
+
/** Lazily create the winkNLP engine exactly once. */
|
|
39
|
+
engine() {
|
|
40
|
+
if (!this.nlp) {
|
|
41
|
+
// `as any` because winkNLP's types declare the model shape as
|
|
42
|
+
// `unknown` internally; the runtime model object is compatible.
|
|
43
|
+
this.nlp = winkNLP(model);
|
|
44
|
+
}
|
|
45
|
+
return this.nlp;
|
|
46
|
+
}
|
|
47
|
+
analyze(input) {
|
|
48
|
+
const trimmed = input?.trim() ?? '';
|
|
49
|
+
if (trimmed === '')
|
|
50
|
+
return { terms: [], phrases: [] };
|
|
51
|
+
const nlp = this.engine();
|
|
52
|
+
const doc = nlp.readDoc(trimmed);
|
|
53
|
+
const values = doc.tokens().out();
|
|
54
|
+
const types = doc.tokens().out(nlp.its.type);
|
|
55
|
+
const pos = doc.tokens().out(nlp.its.pos);
|
|
56
|
+
const stop = doc.tokens().out(nlp.its.stopWordFlag);
|
|
57
|
+
const normal = doc.tokens().out(nlp.its.normal);
|
|
58
|
+
const retained = [];
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
for (let i = 0; i < values.length; i++) {
|
|
61
|
+
// Drop punctuation and non-word tokens.
|
|
62
|
+
if (types[i] !== 'word')
|
|
63
|
+
continue;
|
|
64
|
+
// Drop stop words.
|
|
65
|
+
if (stop[i])
|
|
66
|
+
continue;
|
|
67
|
+
// Keep only meaningful POS.
|
|
68
|
+
if (!RETAINED_POS.has(pos[i]))
|
|
69
|
+
continue;
|
|
70
|
+
// Preserve casing for proper nouns (TypeScript, AWS, MCP,
|
|
71
|
+
// Kubernetes, Terraform, API, …). Lowercase everything else via
|
|
72
|
+
// the `normal` form, which also normalizes simple grammatical
|
|
73
|
+
// variants (case, contractions).
|
|
74
|
+
const term = pos[i] === 'PROPN' ? values[i] : normal[i];
|
|
75
|
+
if (term === '')
|
|
76
|
+
continue;
|
|
77
|
+
// De-duplicate while preserving first-occurrence order.
|
|
78
|
+
if (seen.has(term))
|
|
79
|
+
continue;
|
|
80
|
+
seen.add(term);
|
|
81
|
+
retained.push({ term, index: i });
|
|
82
|
+
}
|
|
83
|
+
const terms = retained.map((t) => t.term);
|
|
84
|
+
const phrases = extractPhrases(retained);
|
|
85
|
+
return { terms, phrases };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Build 2–3 word phrases from consecutive retained tokens that were
|
|
90
|
+
* adjacent in the original document (token indices differ by exactly 1).
|
|
91
|
+
*
|
|
92
|
+
* Longer phrases (trigrams) are emitted before shorter ones (bigrams),
|
|
93
|
+
* and all phrases are de-duplicated while preserving first-occurrence
|
|
94
|
+
* order within each size.
|
|
95
|
+
*/
|
|
96
|
+
function extractPhrases(retained) {
|
|
97
|
+
// Group retained tokens into runs of document-adjacent tokens.
|
|
98
|
+
const runs = [];
|
|
99
|
+
let current = [];
|
|
100
|
+
for (const tok of retained) {
|
|
101
|
+
const prev = current[current.length - 1];
|
|
102
|
+
if (prev && tok.index === prev.index + 1) {
|
|
103
|
+
current.push(tok);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
if (current.length >= 2)
|
|
107
|
+
runs.push(current);
|
|
108
|
+
current = [tok];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (current.length >= 2)
|
|
112
|
+
runs.push(current);
|
|
113
|
+
const phrases = [];
|
|
114
|
+
const seen = new Set();
|
|
115
|
+
// Trigrams first, then bigrams.
|
|
116
|
+
for (const size of [3, 2]) {
|
|
117
|
+
for (const run of runs) {
|
|
118
|
+
for (let i = 0; i + size <= run.length; i++) {
|
|
119
|
+
const phrase = run.slice(i, i + size).map((t) => t.term).join(' ');
|
|
120
|
+
if (!seen.has(phrase)) {
|
|
121
|
+
seen.add(phrase);
|
|
122
|
+
phrases.push(phrase);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return phrases;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Shared singleton analyzer instance.
|
|
131
|
+
*
|
|
132
|
+
* The winkNLP model is heavy to load; reuse one instance across the
|
|
133
|
+
* whole process. Call {@link createWinkQueryAnalyzer} to get a new
|
|
134
|
+
* independent instance if needed (rare).
|
|
135
|
+
*/
|
|
136
|
+
let shared;
|
|
137
|
+
/**
|
|
138
|
+
* Returns a process-wide shared {@link WinkQueryAnalyzer} instance.
|
|
139
|
+
* The NLP model is initialized lazily on first use and reused for all
|
|
140
|
+
* subsequent calls.
|
|
141
|
+
*/
|
|
142
|
+
export function getQueryAnalyzer() {
|
|
143
|
+
if (!shared)
|
|
144
|
+
shared = new WinkQueryAnalyzer();
|
|
145
|
+
return shared;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Creates a fresh, independent {@link WinkQueryAnalyzer} instance.
|
|
149
|
+
*
|
|
150
|
+
* Prefer {@link getQueryAnalyzer} unless you specifically need an
|
|
151
|
+
* isolated instance (e.g. in tests that want to control initialization).
|
|
152
|
+
*/
|
|
153
|
+
export function createWinkQueryAnalyzer() {
|
|
154
|
+
return new WinkQueryAnalyzer();
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=nlp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nlp.js","sourceRoot":"","sources":["../../../../src/extras/extensions/keyword-extractor/nlp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nlp.test.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/keyword-extractor/nlp.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createWinkQueryAnalyzer } from './index.js';
|
|
3
|
+
// Use a fresh analyzer instance for the suite so initialization is
|
|
4
|
+
// controlled and independent of any process-wide singleton.
|
|
5
|
+
const analyzer = createWinkQueryAnalyzer();
|
|
6
|
+
describe('WinkQueryAnalyzer', () => {
|
|
7
|
+
// ── Normal conversational sentence ──────────────────────────────
|
|
8
|
+
it('extracts terms from a normal conversational sentence', () => {
|
|
9
|
+
const out = analyzer.analyze("Can you find the documentation for the TypeScript library we're using to build nested knowledge graphs?");
|
|
10
|
+
expect(out.terms).toEqual([
|
|
11
|
+
'find',
|
|
12
|
+
'documentation',
|
|
13
|
+
'TypeScript',
|
|
14
|
+
'library',
|
|
15
|
+
'build',
|
|
16
|
+
'nested',
|
|
17
|
+
'knowledge',
|
|
18
|
+
'graphs',
|
|
19
|
+
]);
|
|
20
|
+
// Trigram first, then bigrams.
|
|
21
|
+
expect(out.phrases).toEqual([
|
|
22
|
+
'build nested knowledge',
|
|
23
|
+
'nested knowledge graphs',
|
|
24
|
+
'TypeScript library',
|
|
25
|
+
'build nested',
|
|
26
|
+
'nested knowledge',
|
|
27
|
+
'knowledge graphs',
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
// ── Heavy conversational filler ─────────────────────────────────
|
|
31
|
+
it('drops conversational filler and keeps meaningful terms', () => {
|
|
32
|
+
const out = analyzer.analyze('Hey, could you please look through our documentation and find the thing we discussed yesterday about MCP authentication?');
|
|
33
|
+
// Filler (hey, could, you, please, through, our, and, the, we,
|
|
34
|
+
// yesterday, about) is gone. "look" is a real verb and is retained.
|
|
35
|
+
expect(out.terms).toEqual([
|
|
36
|
+
'look',
|
|
37
|
+
'documentation',
|
|
38
|
+
'find',
|
|
39
|
+
'thing',
|
|
40
|
+
'discussed',
|
|
41
|
+
'MCP',
|
|
42
|
+
'authentication',
|
|
43
|
+
]);
|
|
44
|
+
expect(out.phrases).toEqual(['MCP authentication']);
|
|
45
|
+
});
|
|
46
|
+
// ── Technical terms ─────────────────────────────────────────────
|
|
47
|
+
it('preserves technical/proper-noun terms with their casing', () => {
|
|
48
|
+
const out = analyzer.analyze('AWS Kubernetes Terraform API MCP JavaScript');
|
|
49
|
+
expect(out.terms).toEqual([
|
|
50
|
+
'AWS',
|
|
51
|
+
'Kubernetes',
|
|
52
|
+
'Terraform',
|
|
53
|
+
'API',
|
|
54
|
+
'MCP',
|
|
55
|
+
'JavaScript',
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
// ── Acronyms ────────────────────────────────────────────────────
|
|
59
|
+
it('preserves acronyms as proper nouns', () => {
|
|
60
|
+
const out = analyzer.analyze('What is the MCP?');
|
|
61
|
+
expect(out.terms).toEqual(['MCP']);
|
|
62
|
+
expect(out.phrases).toEqual([]);
|
|
63
|
+
});
|
|
64
|
+
// ── Duplicate words ─────────────────────────────────────────────
|
|
65
|
+
it('removes duplicate terms (same casing)', () => {
|
|
66
|
+
const out = analyzer.analyze('MCP, MCP, MCP');
|
|
67
|
+
expect(out.terms).toEqual(['MCP']);
|
|
68
|
+
});
|
|
69
|
+
it('removes duplicate non-stopwords across a sentence', () => {
|
|
70
|
+
const out = analyzer.analyze('find the documentation, find the thing!');
|
|
71
|
+
expect(out.terms).toEqual(['find', 'documentation', 'thing']);
|
|
72
|
+
});
|
|
73
|
+
// ── Punctuation ─────────────────────────────────────────────────
|
|
74
|
+
it('strips punctuation and keeps only word tokens', () => {
|
|
75
|
+
const out = analyzer.analyze('Hello, world!!! How are you doing today?');
|
|
76
|
+
// "Hello" (INTJ) and "today" (ADV) are dropped by the POS filter;
|
|
77
|
+
// punctuation is dropped by the word-type filter.
|
|
78
|
+
expect(out.terms).toEqual(['world']);
|
|
79
|
+
expect(out.phrases).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
// ── Empty string ────────────────────────────────────────────────
|
|
82
|
+
it('returns empty terms for an empty string', () => {
|
|
83
|
+
const out = analyzer.analyze('');
|
|
84
|
+
expect(out.terms).toEqual([]);
|
|
85
|
+
expect(out.phrases).toEqual([]);
|
|
86
|
+
});
|
|
87
|
+
it('returns empty terms for whitespace-only input', () => {
|
|
88
|
+
const out = analyzer.analyze(' ');
|
|
89
|
+
expect(out.terms).toEqual([]);
|
|
90
|
+
expect(out.phrases).toEqual([]);
|
|
91
|
+
});
|
|
92
|
+
it('returns empty terms for nullish input', () => {
|
|
93
|
+
const out = analyzer.analyze(undefined);
|
|
94
|
+
expect(out.terms).toEqual([]);
|
|
95
|
+
expect(out.phrases).toEqual([]);
|
|
96
|
+
});
|
|
97
|
+
// ── Very short input ────────────────────────────────────────────
|
|
98
|
+
it('handles very short input (single filler word)', () => {
|
|
99
|
+
const out = analyzer.analyze('Hi');
|
|
100
|
+
// "Hi" is an interjection — dropped by the POS filter.
|
|
101
|
+
expect(out.terms).toEqual([]);
|
|
102
|
+
});
|
|
103
|
+
it('handles very short input (single stop-word-only phrase)', () => {
|
|
104
|
+
const out = analyzer.analyze('the and of');
|
|
105
|
+
expect(out.terms).toEqual([]);
|
|
106
|
+
});
|
|
107
|
+
// ── A question ──────────────────────────────────────────────────
|
|
108
|
+
it('extracts terms from a question about agent memory', () => {
|
|
109
|
+
const out = analyzer.analyze('Where did we decide to store agent memory?');
|
|
110
|
+
expect(out.terms).toEqual(['decide', 'store', 'agent', 'memory']);
|
|
111
|
+
expect(out.phrases).toEqual([
|
|
112
|
+
'store agent memory',
|
|
113
|
+
'store agent',
|
|
114
|
+
'agent memory',
|
|
115
|
+
]);
|
|
116
|
+
});
|
|
117
|
+
// ── Multi-word technical concepts ───────────────────────────────
|
|
118
|
+
it('extracts multi-word technical concepts as phrases', () => {
|
|
119
|
+
const out = analyzer.analyze('Can you find our documentation about the MCP server architecture?');
|
|
120
|
+
expect(out.terms).toEqual([
|
|
121
|
+
'find',
|
|
122
|
+
'documentation',
|
|
123
|
+
'MCP',
|
|
124
|
+
'server',
|
|
125
|
+
'architecture',
|
|
126
|
+
]);
|
|
127
|
+
expect(out.phrases).toEqual([
|
|
128
|
+
'MCP server architecture',
|
|
129
|
+
'MCP server',
|
|
130
|
+
'server architecture',
|
|
131
|
+
]);
|
|
132
|
+
});
|
|
133
|
+
it('extracts phrases from "nested knowledge graph"', () => {
|
|
134
|
+
const out = analyzer.analyze('nested knowledge graph');
|
|
135
|
+
expect(out.terms).toEqual(['nested', 'knowledge', 'graph']);
|
|
136
|
+
expect(out.phrases).toEqual([
|
|
137
|
+
'nested knowledge graph',
|
|
138
|
+
'nested knowledge',
|
|
139
|
+
'knowledge graph',
|
|
140
|
+
]);
|
|
141
|
+
});
|
|
142
|
+
// ── Mixed casing ────────────────────────────────────────────────
|
|
143
|
+
it('treats different surface casings of a proper noun as distinct terms', () => {
|
|
144
|
+
// winkNLP tags "typescript" (lowercase) as a proper noun too, so
|
|
145
|
+
// each surface form is preserved verbatim and not collapsed.
|
|
146
|
+
const out = analyzer.analyze('TypeScript typescript TYPESCRIPT');
|
|
147
|
+
expect(out.terms).toEqual(['TypeScript', 'typescript', 'TYPESCRIPT']);
|
|
148
|
+
});
|
|
149
|
+
// ── Determinism ─────────────────────────────────────────────────
|
|
150
|
+
it('is deterministic — same input yields same output across calls', () => {
|
|
151
|
+
const input = 'Can you find our documentation about the MCP server architecture?';
|
|
152
|
+
const a = analyzer.analyze(input);
|
|
153
|
+
const b = analyzer.analyze(input);
|
|
154
|
+
expect(b).toEqual(a);
|
|
155
|
+
});
|
|
156
|
+
it('is safe to reuse repeatedly (long-running process)', () => {
|
|
157
|
+
const analyzer2 = createWinkQueryAnalyzer();
|
|
158
|
+
const samples = [
|
|
159
|
+
'Find the MCP documentation',
|
|
160
|
+
'Where did we store agent memory?',
|
|
161
|
+
'AWS Kubernetes Terraform',
|
|
162
|
+
'',
|
|
163
|
+
'nested knowledge graph',
|
|
164
|
+
];
|
|
165
|
+
// Run many times — should never throw and stay deterministic.
|
|
166
|
+
for (let i = 0; i < 50; i++) {
|
|
167
|
+
for (const s of samples) {
|
|
168
|
+
const out = analyzer2.analyze(s);
|
|
169
|
+
expect(Array.isArray(out.terms)).toBe(true);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Final call matches a fresh call on another instance.
|
|
173
|
+
expect(analyzer2.analyze('nested knowledge graph')).toEqual(analyzer.analyze('nested knowledge graph'));
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
//# sourceMappingURL=nlp.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nlp.test.js","sourceRoot":"","sources":["../../../../src/extras/extensions/keyword-extractor/nlp.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Extension } from '../../../extension.js';
|
|
2
|
+
/**
|
|
3
|
+
* Log level for the logging extension.
|
|
4
|
+
*
|
|
5
|
+
* - `'info'` — turn start/finish, errors (high-level lifecycle)
|
|
6
|
+
* - `'debug'` — per-LLM-call and per-tool-call details (verbose)
|
|
7
|
+
*/
|
|
8
|
+
export type LogLevel = 'info' | 'debug';
|
|
9
|
+
/**
|
|
10
|
+
* Config for the logging extension.
|
|
11
|
+
*
|
|
12
|
+
* Passed by the extension loader from the host's config object.
|
|
13
|
+
* Keys should be declared in extension.json's `configKeys`.
|
|
14
|
+
*/
|
|
15
|
+
export interface LoggingExtensionConfig {
|
|
16
|
+
/**
|
|
17
|
+
* Custom log function. Default: `console.log`.
|
|
18
|
+
* Useful for routing logs to a file, transport, or structured logger.
|
|
19
|
+
*/
|
|
20
|
+
logFunction?: (...args: unknown[]) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Custom error function. Default: `console.error`.
|
|
23
|
+
*/
|
|
24
|
+
errorFunction?: (...args: unknown[]) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Prefix for log lines. Default: `"[logging]"`.
|
|
27
|
+
*/
|
|
28
|
+
logPrefix?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Minimum log level to emit. Default: `'info'`.
|
|
31
|
+
*
|
|
32
|
+
* Set to `'debug'` to see per-LLM-call and per-tool-call details.
|
|
33
|
+
* If the `DEBUG` environment variable is set, defaults to `'debug'`.
|
|
34
|
+
*/
|
|
35
|
+
level?: LogLevel;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Logging extension — logs each lifecycle stage.
|
|
39
|
+
*
|
|
40
|
+
* Observes every hook stage (beforeTurn, beforeLLM, afterLLM, beforeTool,
|
|
41
|
+
* afterTool, onError, afterTurn) without mutating anything. Accepts
|
|
42
|
+
* optional config for custom log/error functions, a prefix, and a log level.
|
|
43
|
+
*
|
|
44
|
+
* Log levels:
|
|
45
|
+
* - **info** (default): turn start/finish, errors
|
|
46
|
+
* - **debug**: per-LLM-call and per-tool-call details
|
|
47
|
+
*
|
|
48
|
+
* If the `DEBUG` environment variable is set and no explicit `level` is
|
|
49
|
+
* provided, the level defaults to `'debug'`.
|
|
50
|
+
*/
|
|
51
|
+
export default function createLoggingExtension(config?: LoggingExtensionConfig): Extension;
|
|
52
|
+
//# sourceMappingURL=extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/logger/extension.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAExC;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAC7C;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAID;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,UAAU,sBAAsB,CAC5C,MAAM,CAAC,EAAE,sBAAsB,GAC9B,SAAS,CAgDX"}
|