comisai 1.0.10 → 1.0.12
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/README.md +3 -3
- package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +1 -4
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/dist/wizard/steps/06-channels.js +88 -11
- package/node_modules/@comis/cli/dist/wizard/steps/10-write-config.js +4 -0
- package/node_modules/@comis/cli/dist/wizard/types.d.ts +1 -0
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/package.json +1 -1
- package/node_modules/@comis/gateway/package.json +1 -1
- package/node_modules/@comis/infra/package.json +1 -1
- package/node_modules/@comis/memory/package.json +1 -1
- package/node_modules/@comis/scheduler/package.json +1 -1
- package/node_modules/@comis/shared/package.json +1 -1
- package/node_modules/@comis/skills/package.json +1 -1
- package/package.json +18 -18
- package/node_modules/@comis/core/dist/approval/approval-gate.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/approval/approval-gate.test.js +0 -1003
- package/node_modules/@comis/core/dist/bootstrap.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/bootstrap.test.js +0 -150
- package/node_modules/@comis/core/dist/config/backup.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/backup.test.js +0 -160
- package/node_modules/@comis/core/dist/config/env-substitution.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/env-substitution.test.js +0 -259
- package/node_modules/@comis/core/dist/config/field-metadata.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/field-metadata.test.js +0 -72
- package/node_modules/@comis/core/dist/config/git-manager.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/git-manager.test.js +0 -1042
- package/node_modules/@comis/core/dist/config/immutable-keys.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/immutable-keys.test.js +0 -283
- package/node_modules/@comis/core/dist/config/include-resolver.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/include-resolver.test.js +0 -292
- package/node_modules/@comis/core/dist/config/layered.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/layered.test.js +0 -211
- package/node_modules/@comis/core/dist/config/loader.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/loader.test.js +0 -300
- package/node_modules/@comis/core/dist/config/migrate.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/migrate.test.js +0 -244
- package/node_modules/@comis/core/dist/config/partial-validator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/partial-validator.test.js +0 -98
- package/node_modules/@comis/core/dist/config/schema-agent-model.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-agent-model.test.js +0 -279
- package/node_modules/@comis/core/dist/config/schema-agent-session.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-agent-session.test.js +0 -242
- package/node_modules/@comis/core/dist/config/schema-agent.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-agent.test.js +0 -645
- package/node_modules/@comis/core/dist/config/schema-channel.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-channel.test.js +0 -341
- package/node_modules/@comis/core/dist/config/schema-coalescer.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-coalescer.test.js +0 -51
- package/node_modules/@comis/core/dist/config/schema-context-engine.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-context-engine.test.js +0 -797
- package/node_modules/@comis/core/dist/config/schema-context-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-context-guard.test.js +0 -111
- package/node_modules/@comis/core/dist/config/schema-daemon.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-daemon.test.js +0 -107
- package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.js +0 -51
- package/node_modules/@comis/core/dist/config/schema-documentation.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-documentation.test.js +0 -63
- package/node_modules/@comis/core/dist/config/schema-gateway.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-gateway.test.js +0 -219
- package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.js +0 -41
- package/node_modules/@comis/core/dist/config/schema-integrations.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-integrations.test.js +0 -92
- package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.js +0 -61
- package/node_modules/@comis/core/dist/config/schema-misc.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-misc.test.js +0 -394
- package/node_modules/@comis/core/dist/config/schema-observability.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-observability.test.js +0 -76
- package/node_modules/@comis/core/dist/config/schema-providers.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-providers.test.js +0 -294
- package/node_modules/@comis/core/dist/config/schema-queue.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-queue.test.js +0 -234
- package/node_modules/@comis/core/dist/config/schema-response-prefix.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-response-prefix.test.js +0 -36
- package/node_modules/@comis/core/dist/config/schema-security.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-security.test.js +0 -54
- package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.js +0 -60
- package/node_modules/@comis/core/dist/config/schema-serializer.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-serializer.test.js +0 -73
- package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.js +0 -39
- package/node_modules/@comis/core/dist/context/context.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/context/context.test.js +0 -276
- package/node_modules/@comis/core/dist/domain/agent-response.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/agent-response.test.js +0 -159
- package/node_modules/@comis/core/dist/domain/credential-mapping.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/credential-mapping.test.js +0 -135
- package/node_modules/@comis/core/dist/domain/delivery-origin.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/delivery-origin.test.js +0 -68
- package/node_modules/@comis/core/dist/domain/execution-graph.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/execution-graph.test.js +0 -441
- package/node_modules/@comis/core/dist/domain/memory-entry.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/memory-entry.test.js +0 -193
- package/node_modules/@comis/core/dist/domain/normalized-message.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/normalized-message.test.js +0 -255
- package/node_modules/@comis/core/dist/domain/session-key.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/session-key.test.js +0 -291
- package/node_modules/@comis/core/dist/domain/subagent-context-config.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/subagent-context-config.test.js +0 -131
- package/node_modules/@comis/core/dist/domain/subagent-context-types.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/subagent-context-types.test.js +0 -182
- package/node_modules/@comis/core/dist/event-bus/bus.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/bus.test.js +0 -152
- package/node_modules/@comis/core/dist/event-bus/events-agent.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events-agent.test.js +0 -409
- package/node_modules/@comis/core/dist/event-bus/events-channel.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events-channel.test.js +0 -240
- package/node_modules/@comis/core/dist/event-bus/events-infra.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events-infra.test.js +0 -416
- package/node_modules/@comis/core/dist/event-bus/events-messaging.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events-messaging.test.js +0 -433
- package/node_modules/@comis/core/dist/event-bus/events.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events.test.js +0 -93
- package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.js +0 -29
- package/node_modules/@comis/core/dist/hooks/hook-runner.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/hook-runner.test.js +0 -490
- package/node_modules/@comis/core/dist/hooks/hook-strategies.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/hook-strategies.test.js +0 -209
- package/node_modules/@comis/core/dist/hooks/integration.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/integration.test.js +0 -235
- package/node_modules/@comis/core/dist/hooks/plugin-registry.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/plugin-registry.test.js +0 -470
- package/node_modules/@comis/core/dist/load-env.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/load-env.test.js +0 -21
- package/node_modules/@comis/core/dist/security/action-classifier.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/action-classifier.test.js +0 -298
- package/node_modules/@comis/core/dist/security/audit-aggregator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/audit-aggregator.test.js +0 -128
- package/node_modules/@comis/core/dist/security/audit.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/audit.test.js +0 -154
- package/node_modules/@comis/core/dist/security/canary-token.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/canary-token.test.js +0 -45
- package/node_modules/@comis/core/dist/security/config-redaction.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/config-redaction.test.js +0 -107
- package/node_modules/@comis/core/dist/security/external-content.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/external-content.test.js +0 -210
- package/node_modules/@comis/core/dist/security/injection-patterns.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/injection-patterns.test.js +0 -213
- package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.js +0 -194
- package/node_modules/@comis/core/dist/security/input-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/input-guard.test.js +0 -215
- package/node_modules/@comis/core/dist/security/input-security-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/input-security-guard.test.js +0 -215
- package/node_modules/@comis/core/dist/security/input-validator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/input-validator.test.js +0 -133
- package/node_modules/@comis/core/dist/security/log-sanitizer.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/log-sanitizer.test.js +0 -260
- package/node_modules/@comis/core/dist/security/memory-write-validator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/memory-write-validator.test.js +0 -62
- package/node_modules/@comis/core/dist/security/output-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/output-guard.test.js +0 -397
- package/node_modules/@comis/core/dist/security/safe-path.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/safe-path.test.js +0 -95
- package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.js +0 -231
- package/node_modules/@comis/core/dist/security/secret-access.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/secret-access.test.js +0 -41
- package/node_modules/@comis/core/dist/security/secret-crypto.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/secret-crypto.test.js +0 -113
- package/node_modules/@comis/core/dist/security/secret-manager.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/secret-manager.test.js +0 -394
- package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.js +0 -268
- package/node_modules/@comis/core/dist/security/secrets-audit.test.d.ts +0 -10
- package/node_modules/@comis/core/dist/security/secrets-audit.test.js +0 -295
- package/node_modules/@comis/core/dist/security/ssrf-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/ssrf-guard.test.js +0 -127
- package/node_modules/@comis/core/dist/security/token-generator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/token-generator.test.js +0 -35
- package/node_modules/@comis/core/dist/tool-metadata.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/tool-metadata.test.js +0 -112
- package/node_modules/@comis/memory/dist/compaction.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/compaction.test.js +0 -298
- package/node_modules/@comis/memory/dist/context-schema.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/context-schema.test.js +0 -246
- package/node_modules/@comis/memory/dist/context-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/context-store.test.js +0 -708
- package/node_modules/@comis/memory/dist/credential-mapping-schema.test.d.ts +0 -7
- package/node_modules/@comis/memory/dist/credential-mapping-schema.test.js +0 -73
- package/node_modules/@comis/memory/dist/credential-mapping-store.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/credential-mapping-store.test.js +0 -340
- package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.js +0 -165
- package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.js +0 -263
- package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.js +0 -202
- package/node_modules/@comis/memory/dist/embedding-cache-lru.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-cache-lru.test.js +0 -241
- package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.js +0 -678
- package/node_modules/@comis/memory/dist/embedding-cache.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-cache.test.js +0 -213
- package/node_modules/@comis/memory/dist/embedding-fingerprint.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-fingerprint.test.js +0 -87
- package/node_modules/@comis/memory/dist/embedding-hash.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-hash.test.js +0 -31
- package/node_modules/@comis/memory/dist/embedding-integration.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/embedding-integration.test.js +0 -232
- package/node_modules/@comis/memory/dist/embedding-provider-factory.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-provider-factory.test.js +0 -176
- package/node_modules/@comis/memory/dist/embedding-provider-local.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/embedding-provider-local.test.js +0 -76
- package/node_modules/@comis/memory/dist/embedding-provider-openai.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/embedding-provider-openai.test.js +0 -100
- package/node_modules/@comis/memory/dist/embedding-queue.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-queue.test.js +0 -236
- package/node_modules/@comis/memory/dist/hybrid-search.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/hybrid-search.test.js +0 -476
- package/node_modules/@comis/memory/dist/identity-link-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/identity-link-store.test.js +0 -88
- package/node_modules/@comis/memory/dist/memory-api.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/memory-api.test.js +0 -495
- package/node_modules/@comis/memory/dist/memory-concurrency.test.d.ts +0 -20
- package/node_modules/@comis/memory/dist/memory-concurrency.test.js +0 -222
- package/node_modules/@comis/memory/dist/named-graph-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/named-graph-store.test.js +0 -227
- package/node_modules/@comis/memory/dist/observability-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/observability-store.test.js +0 -704
- package/node_modules/@comis/memory/dist/row-mapper.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/row-mapper.test.js +0 -409
- package/node_modules/@comis/memory/dist/schema.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/schema.test.js +0 -240
- package/node_modules/@comis/memory/dist/secret-store-schema.test.d.ts +0 -7
- package/node_modules/@comis/memory/dist/secret-store-schema.test.js +0 -90
- package/node_modules/@comis/memory/dist/session-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/session-store.test.js +0 -262
- package/node_modules/@comis/memory/dist/setup-secrets.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/setup-secrets.test.js +0 -140
- package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.js +0 -888
- package/node_modules/@comis/memory/dist/sqlite-secret-store.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/sqlite-secret-store.test.js +0 -245
- package/node_modules/@comis/shared/dist/abort.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/abort.test.js +0 -71
- package/node_modules/@comis/shared/dist/result.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/result.test.js +0 -178
- package/node_modules/@comis/shared/dist/suppress-error.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/suppress-error.test.js +0 -50
- package/node_modules/@comis/shared/dist/timeout.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/timeout.test.js +0 -75
- package/node_modules/@comis/shared/dist/ttl-cache.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/ttl-cache.test.js +0 -81
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { ok, err } from "@comis/shared";
|
|
3
|
-
// Mock the local and openai provider modules
|
|
4
|
-
vi.mock("./embedding-provider-local.js", () => ({
|
|
5
|
-
createLocalEmbeddingProvider: vi.fn(),
|
|
6
|
-
}));
|
|
7
|
-
vi.mock("./embedding-provider-openai.js", () => ({
|
|
8
|
-
createOpenAIEmbeddingProvider: vi.fn(),
|
|
9
|
-
}));
|
|
10
|
-
import { createEmbeddingProvider } from "./embedding-provider-factory.js";
|
|
11
|
-
import { createLocalEmbeddingProvider } from "./embedding-provider-local.js";
|
|
12
|
-
import { createOpenAIEmbeddingProvider } from "./embedding-provider-openai.js";
|
|
13
|
-
const mockLocal = vi.mocked(createLocalEmbeddingProvider);
|
|
14
|
-
const mockOpenAI = vi.mocked(createOpenAIEmbeddingProvider);
|
|
15
|
-
function stubPort(overrides) {
|
|
16
|
-
return {
|
|
17
|
-
provider: "test",
|
|
18
|
-
dimensions: 768,
|
|
19
|
-
modelId: "test-model",
|
|
20
|
-
embed: vi.fn().mockResolvedValue(ok([0.1, 0.2])),
|
|
21
|
-
embedBatch: vi.fn().mockResolvedValue(ok([[0.1], [0.2]])),
|
|
22
|
-
...overrides,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
describe("createEmbeddingProvider", () => {
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
vi.clearAllMocks();
|
|
28
|
-
});
|
|
29
|
-
describe("auto mode", () => {
|
|
30
|
-
it("selects local when local succeeds", async () => {
|
|
31
|
-
const localPort = stubPort({ modelId: "local-model" });
|
|
32
|
-
mockLocal.mockResolvedValue(ok(localPort));
|
|
33
|
-
const result = await createEmbeddingProvider({
|
|
34
|
-
provider: "auto",
|
|
35
|
-
local: { modelUri: "hf:test", modelsDir: "models" },
|
|
36
|
-
remote: { apiKey: "sk-test", model: "text-embedding-3-small", dimensions: 1536 },
|
|
37
|
-
});
|
|
38
|
-
expect(result.ok).toBe(true);
|
|
39
|
-
if (result.ok) {
|
|
40
|
-
expect(result.value.modelId).toBe("local-model");
|
|
41
|
-
}
|
|
42
|
-
expect(mockLocal).toHaveBeenCalledOnce();
|
|
43
|
-
// Remote should NOT have been called since local succeeded
|
|
44
|
-
expect(mockOpenAI).not.toHaveBeenCalled();
|
|
45
|
-
});
|
|
46
|
-
it("falls back to remote when local fails", async () => {
|
|
47
|
-
mockLocal.mockResolvedValue(err(new Error("node-llama-cpp not available")));
|
|
48
|
-
const remotePort = stubPort({ modelId: "openai-model", dimensions: 1536 });
|
|
49
|
-
mockOpenAI.mockReturnValue(ok(remotePort));
|
|
50
|
-
const result = await createEmbeddingProvider({
|
|
51
|
-
provider: "auto",
|
|
52
|
-
local: { modelUri: "hf:test", modelsDir: "models" },
|
|
53
|
-
remote: { apiKey: "sk-test", model: "text-embedding-3-small", dimensions: 1536 },
|
|
54
|
-
});
|
|
55
|
-
expect(result.ok).toBe(true);
|
|
56
|
-
if (result.ok) {
|
|
57
|
-
expect(result.value.modelId).toBe("openai-model");
|
|
58
|
-
}
|
|
59
|
-
expect(mockLocal).toHaveBeenCalledOnce();
|
|
60
|
-
expect(mockOpenAI).toHaveBeenCalledOnce();
|
|
61
|
-
});
|
|
62
|
-
it("returns error when both fail", async () => {
|
|
63
|
-
mockLocal.mockResolvedValue(err(new Error("native bindings unavailable")));
|
|
64
|
-
mockOpenAI.mockReturnValue(err(new Error("OpenAI embedding provider requires an apiKey")));
|
|
65
|
-
const result = await createEmbeddingProvider({
|
|
66
|
-
provider: "auto",
|
|
67
|
-
local: { modelUri: "hf:test", modelsDir: "models" },
|
|
68
|
-
remote: { apiKey: "", model: "text-embedding-3-small", dimensions: 1536 },
|
|
69
|
-
});
|
|
70
|
-
expect(result.ok).toBe(false);
|
|
71
|
-
if (!result.ok) {
|
|
72
|
-
expect(result.error.message).toContain("No embedding provider available");
|
|
73
|
-
expect(result.error.message).toContain("native bindings unavailable");
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
it("skips local when no local config provided", async () => {
|
|
77
|
-
const remotePort = stubPort({ modelId: "openai-fallback" });
|
|
78
|
-
mockOpenAI.mockReturnValue(ok(remotePort));
|
|
79
|
-
const result = await createEmbeddingProvider({
|
|
80
|
-
provider: "auto",
|
|
81
|
-
remote: { apiKey: "sk-test", model: "text-embedding-3-small", dimensions: 1536 },
|
|
82
|
-
});
|
|
83
|
-
expect(result.ok).toBe(true);
|
|
84
|
-
if (result.ok) {
|
|
85
|
-
expect(result.value.modelId).toBe("openai-fallback");
|
|
86
|
-
}
|
|
87
|
-
expect(mockLocal).not.toHaveBeenCalled();
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
describe("local mode", () => {
|
|
91
|
-
it("returns error when local fails (no fallback)", async () => {
|
|
92
|
-
mockLocal.mockResolvedValue(err(new Error("model file not found")));
|
|
93
|
-
const result = await createEmbeddingProvider({
|
|
94
|
-
provider: "local",
|
|
95
|
-
local: { modelUri: "hf:test", modelsDir: "models" },
|
|
96
|
-
remote: { apiKey: "sk-test", model: "text-embedding-3-small", dimensions: 1536 },
|
|
97
|
-
});
|
|
98
|
-
expect(result.ok).toBe(false);
|
|
99
|
-
if (!result.ok) {
|
|
100
|
-
expect(result.error.message).toBe("model file not found");
|
|
101
|
-
}
|
|
102
|
-
// Remote should NOT be attempted for explicit local mode
|
|
103
|
-
expect(mockOpenAI).not.toHaveBeenCalled();
|
|
104
|
-
});
|
|
105
|
-
it("returns error when no local config provided", async () => {
|
|
106
|
-
const result = await createEmbeddingProvider({
|
|
107
|
-
provider: "local",
|
|
108
|
-
});
|
|
109
|
-
expect(result.ok).toBe(false);
|
|
110
|
-
if (!result.ok) {
|
|
111
|
-
expect(result.error.message).toContain("no local config provided");
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
it("succeeds when local provider works", async () => {
|
|
115
|
-
const localPort = stubPort({ modelId: "local-gguf" });
|
|
116
|
-
mockLocal.mockResolvedValue(ok(localPort));
|
|
117
|
-
const result = await createEmbeddingProvider({
|
|
118
|
-
provider: "local",
|
|
119
|
-
local: { modelUri: "/path/to/model.gguf", modelsDir: "models" },
|
|
120
|
-
});
|
|
121
|
-
expect(result.ok).toBe(true);
|
|
122
|
-
if (result.ok) {
|
|
123
|
-
expect(result.value.modelId).toBe("local-gguf");
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
describe("openai mode", () => {
|
|
128
|
-
it("returns error when no apiKey provided", async () => {
|
|
129
|
-
const result = await createEmbeddingProvider({
|
|
130
|
-
provider: "openai",
|
|
131
|
-
});
|
|
132
|
-
expect(result.ok).toBe(false);
|
|
133
|
-
if (!result.ok) {
|
|
134
|
-
expect(result.error.message).toContain("apiKey");
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
it("returns error when apiKey is empty string", async () => {
|
|
138
|
-
const result = await createEmbeddingProvider({
|
|
139
|
-
provider: "openai",
|
|
140
|
-
remote: { apiKey: "", model: "text-embedding-3-small", dimensions: 1536 },
|
|
141
|
-
});
|
|
142
|
-
expect(result.ok).toBe(false);
|
|
143
|
-
if (!result.ok) {
|
|
144
|
-
expect(result.error.message).toContain("apiKey");
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
it("succeeds with valid config", async () => {
|
|
148
|
-
const remotePort = stubPort({ modelId: "text-embedding-3-small", dimensions: 1536 });
|
|
149
|
-
mockOpenAI.mockReturnValue(ok(remotePort));
|
|
150
|
-
const result = await createEmbeddingProvider({
|
|
151
|
-
provider: "openai",
|
|
152
|
-
remote: { apiKey: "sk-valid-key", model: "text-embedding-3-small", dimensions: 1536 },
|
|
153
|
-
});
|
|
154
|
-
expect(result.ok).toBe(true);
|
|
155
|
-
if (result.ok) {
|
|
156
|
-
expect(result.value.modelId).toBe("text-embedding-3-small");
|
|
157
|
-
expect(result.value.dimensions).toBe(1536);
|
|
158
|
-
}
|
|
159
|
-
expect(mockOpenAI).toHaveBeenCalledWith({
|
|
160
|
-
apiKey: "sk-valid-key",
|
|
161
|
-
model: "text-embedding-3-small",
|
|
162
|
-
dimensions: 1536,
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
it("does not attempt local provider", async () => {
|
|
166
|
-
const remotePort = stubPort();
|
|
167
|
-
mockOpenAI.mockReturnValue(ok(remotePort));
|
|
168
|
-
await createEmbeddingProvider({
|
|
169
|
-
provider: "openai",
|
|
170
|
-
local: { modelUri: "hf:test", modelsDir: "models" },
|
|
171
|
-
remote: { apiKey: "sk-test", model: "text-embedding-3-small", dimensions: 1536 },
|
|
172
|
-
});
|
|
173
|
-
expect(mockLocal).not.toHaveBeenCalled();
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Contract tests for the local (node-llama-cpp) embedding provider.
|
|
3
|
-
*
|
|
4
|
-
* Tests gated behind LLAMA_MODEL_PATH are silently skipped in normal
|
|
5
|
-
* CI runs and only execute when the env var points to a valid GGUF model.
|
|
6
|
-
* The invalid-model-path error handling test runs unconditionally.
|
|
7
|
-
*/
|
|
8
|
-
export {};
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Contract tests for the local (node-llama-cpp) embedding provider.
|
|
3
|
-
*
|
|
4
|
-
* Tests gated behind LLAMA_MODEL_PATH are silently skipped in normal
|
|
5
|
-
* CI runs and only execute when the env var points to a valid GGUF model.
|
|
6
|
-
* The invalid-model-path error handling test runs unconditionally.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it, expect } from "vitest";
|
|
9
|
-
import { createLocalEmbeddingProvider } from "./embedding-provider-local.js";
|
|
10
|
-
const LLAMA_MODEL_PATH = process.env.LLAMA_MODEL_PATH;
|
|
11
|
-
describe.skipIf(!LLAMA_MODEL_PATH)("Local embedding provider (contract test)", () => {
|
|
12
|
-
it("creates provider successfully with valid model path", async () => {
|
|
13
|
-
const result = await createLocalEmbeddingProvider({
|
|
14
|
-
modelUri: LLAMA_MODEL_PATH,
|
|
15
|
-
modelsDir: "/tmp/comis-test-models",
|
|
16
|
-
});
|
|
17
|
-
expect(result.ok).toBe(true);
|
|
18
|
-
if (result.ok) {
|
|
19
|
-
expect(result.value.dimensions).toBeGreaterThan(0);
|
|
20
|
-
expect(result.value.modelId).toBe(LLAMA_MODEL_PATH);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
it("generates embedding with correct dimensions", async () => {
|
|
24
|
-
const createResult = await createLocalEmbeddingProvider({
|
|
25
|
-
modelUri: LLAMA_MODEL_PATH,
|
|
26
|
-
modelsDir: "/tmp/comis-test-models",
|
|
27
|
-
});
|
|
28
|
-
expect(createResult.ok).toBe(true);
|
|
29
|
-
if (!createResult.ok)
|
|
30
|
-
return;
|
|
31
|
-
const provider = createResult.value;
|
|
32
|
-
const result = await provider.embed("Test text for local embedding");
|
|
33
|
-
expect(result.ok).toBe(true);
|
|
34
|
-
if (result.ok) {
|
|
35
|
-
expect(result.value).toBeInstanceOf(Array);
|
|
36
|
-
expect(result.value.length).toBe(provider.dimensions);
|
|
37
|
-
// All values should be finite numbers
|
|
38
|
-
for (const val of result.value) {
|
|
39
|
-
expect(Number.isFinite(val)).toBe(true);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
it("generates batch embeddings", async () => {
|
|
44
|
-
const createResult = await createLocalEmbeddingProvider({
|
|
45
|
-
modelUri: LLAMA_MODEL_PATH,
|
|
46
|
-
modelsDir: "/tmp/comis-test-models",
|
|
47
|
-
});
|
|
48
|
-
expect(createResult.ok).toBe(true);
|
|
49
|
-
if (!createResult.ok)
|
|
50
|
-
return;
|
|
51
|
-
const provider = createResult.value;
|
|
52
|
-
const result = await provider.embedBatch(["Hello", "World"]);
|
|
53
|
-
expect(result.ok).toBe(true);
|
|
54
|
-
if (result.ok) {
|
|
55
|
-
expect(result.value).toHaveLength(2);
|
|
56
|
-
for (const embedding of result.value) {
|
|
57
|
-
expect(embedding.length).toBe(provider.dimensions);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
// Ungated test: verify error handling with invalid model path
|
|
63
|
-
describe("Local embedding provider (error handling)", () => {
|
|
64
|
-
it("handles invalid model path gracefully", async () => {
|
|
65
|
-
const result = await createLocalEmbeddingProvider({
|
|
66
|
-
modelUri: "/nonexistent/model.gguf",
|
|
67
|
-
modelsDir: "/tmp/comis-test-models",
|
|
68
|
-
});
|
|
69
|
-
// Should return err() with a meaningful error, not crash
|
|
70
|
-
expect(result.ok).toBe(false);
|
|
71
|
-
if (!result.ok) {
|
|
72
|
-
expect(result.error).toBeInstanceOf(Error);
|
|
73
|
-
expect(result.error.message.length).toBeGreaterThan(0);
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Contract tests for the OpenAI embedding provider.
|
|
3
|
-
*
|
|
4
|
-
* These tests call the real OpenAI API and are gated behind the
|
|
5
|
-
* OPENAI_API_KEY environment variable. They are silently skipped
|
|
6
|
-
* in normal CI runs and only execute when the env var is set.
|
|
7
|
-
*/
|
|
8
|
-
export {};
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Contract tests for the OpenAI embedding provider.
|
|
3
|
-
*
|
|
4
|
-
* These tests call the real OpenAI API and are gated behind the
|
|
5
|
-
* OPENAI_API_KEY environment variable. They are silently skipped
|
|
6
|
-
* in normal CI runs and only execute when the env var is set.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it, expect } from "vitest";
|
|
9
|
-
import { createOpenAIEmbeddingProvider } from "./embedding-provider-openai.js";
|
|
10
|
-
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
11
|
-
describe.skipIf(!OPENAI_API_KEY)("OpenAI embedding provider (contract test)", () => {
|
|
12
|
-
it("creates provider successfully", () => {
|
|
13
|
-
const result = createOpenAIEmbeddingProvider({
|
|
14
|
-
apiKey: OPENAI_API_KEY,
|
|
15
|
-
model: "text-embedding-3-small",
|
|
16
|
-
dimensions: 1536,
|
|
17
|
-
});
|
|
18
|
-
expect(result.ok).toBe(true);
|
|
19
|
-
if (result.ok) {
|
|
20
|
-
expect(result.value.dimensions).toBe(1536);
|
|
21
|
-
expect(result.value.modelId).toBe("text-embedding-3-small");
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
it("generates embedding for single text", async () => {
|
|
25
|
-
const createResult = createOpenAIEmbeddingProvider({
|
|
26
|
-
apiKey: OPENAI_API_KEY,
|
|
27
|
-
model: "text-embedding-3-small",
|
|
28
|
-
dimensions: 1536,
|
|
29
|
-
});
|
|
30
|
-
expect(createResult.ok).toBe(true);
|
|
31
|
-
if (!createResult.ok)
|
|
32
|
-
return;
|
|
33
|
-
const provider = createResult.value;
|
|
34
|
-
const result = await provider.embed("Hello world");
|
|
35
|
-
expect(result.ok).toBe(true);
|
|
36
|
-
if (result.ok) {
|
|
37
|
-
expect(result.value).toBeInstanceOf(Array);
|
|
38
|
-
expect(result.value.length).toBe(1536);
|
|
39
|
-
// All values should be finite numbers
|
|
40
|
-
for (const val of result.value) {
|
|
41
|
-
expect(Number.isFinite(val)).toBe(true);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
it("generates embeddings for batch", async () => {
|
|
46
|
-
const createResult = createOpenAIEmbeddingProvider({
|
|
47
|
-
apiKey: OPENAI_API_KEY,
|
|
48
|
-
model: "text-embedding-3-small",
|
|
49
|
-
dimensions: 1536,
|
|
50
|
-
});
|
|
51
|
-
expect(createResult.ok).toBe(true);
|
|
52
|
-
if (!createResult.ok)
|
|
53
|
-
return;
|
|
54
|
-
const provider = createResult.value;
|
|
55
|
-
const result = await provider.embedBatch(["Hello", "World"]);
|
|
56
|
-
expect(result.ok).toBe(true);
|
|
57
|
-
if (result.ok) {
|
|
58
|
-
expect(result.value).toHaveLength(2);
|
|
59
|
-
for (const embedding of result.value) {
|
|
60
|
-
expect(embedding.length).toBe(1536);
|
|
61
|
-
for (const val of embedding) {
|
|
62
|
-
expect(Number.isFinite(val)).toBe(true);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
it("returns error for empty text", async () => {
|
|
68
|
-
const createResult = createOpenAIEmbeddingProvider({
|
|
69
|
-
apiKey: OPENAI_API_KEY,
|
|
70
|
-
model: "text-embedding-3-small",
|
|
71
|
-
dimensions: 1536,
|
|
72
|
-
});
|
|
73
|
-
expect(createResult.ok).toBe(true);
|
|
74
|
-
if (!createResult.ok)
|
|
75
|
-
return;
|
|
76
|
-
const provider = createResult.value;
|
|
77
|
-
const result = await provider.embed("");
|
|
78
|
-
// OpenAI may reject empty strings with an error, or return a zero vector.
|
|
79
|
-
// Either behavior is acceptable -- we verify it does not crash.
|
|
80
|
-
if (!result.ok) {
|
|
81
|
-
expect(result.error).toBeInstanceOf(Error);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
expect(result.value.length).toBe(1536);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
// Ungated test: verify provider creation fails with bad API key format
|
|
89
|
-
describe("OpenAI embedding provider (error handling)", () => {
|
|
90
|
-
it("creates provider with any apiKey format (validation is lazy)", () => {
|
|
91
|
-
// The OpenAI SDK accepts any string as apiKey at construction time.
|
|
92
|
-
// Actual validation happens at call time. Verify creation succeeds.
|
|
93
|
-
const result = createOpenAIEmbeddingProvider({
|
|
94
|
-
apiKey: "sk-invalid-key-for-testing",
|
|
95
|
-
model: "text-embedding-3-small",
|
|
96
|
-
dimensions: 1536,
|
|
97
|
-
});
|
|
98
|
-
expect(result.ok).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import { ok, err } from "@comis/shared";
|
|
2
|
-
import Database from "better-sqlite3";
|
|
3
|
-
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
4
|
-
import { createEmbeddingQueue } from "./embedding-queue.js";
|
|
5
|
-
import { initSchema, isVecAvailable } from "./schema.js";
|
|
6
|
-
const DIMS = 4;
|
|
7
|
-
/**
|
|
8
|
-
* Create a mock EmbeddingPort that returns deterministic embeddings.
|
|
9
|
-
* Tracks call order via the `calls` array for FIFO verification.
|
|
10
|
-
*/
|
|
11
|
-
function createMockEmbeddingPort(opts) {
|
|
12
|
-
const calls = [];
|
|
13
|
-
const delay = opts?.delay ?? 0;
|
|
14
|
-
const fail = opts?.fail ?? false;
|
|
15
|
-
return {
|
|
16
|
-
provider: "test",
|
|
17
|
-
dimensions: DIMS,
|
|
18
|
-
modelId: "test-embed-model",
|
|
19
|
-
calls,
|
|
20
|
-
async embed(text) {
|
|
21
|
-
calls.push(text);
|
|
22
|
-
if (delay > 0) {
|
|
23
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
24
|
-
}
|
|
25
|
-
if (fail) {
|
|
26
|
-
return err(new Error("mock embedding failure"));
|
|
27
|
-
}
|
|
28
|
-
// Deterministic vector based on text chars
|
|
29
|
-
const vec = new Array(DIMS).fill(0);
|
|
30
|
-
for (let i = 0; i < text.length && i < DIMS; i++) {
|
|
31
|
-
vec[i] = text.charCodeAt(i) / 256;
|
|
32
|
-
}
|
|
33
|
-
return ok(vec);
|
|
34
|
-
},
|
|
35
|
-
async embedBatch(texts) {
|
|
36
|
-
const results = [];
|
|
37
|
-
for (const t of texts) {
|
|
38
|
-
const r = await this.embed(t);
|
|
39
|
-
if (r.ok)
|
|
40
|
-
results.push(r.value);
|
|
41
|
-
}
|
|
42
|
-
return ok(results);
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
/** Insert a minimal memory row into the database. */
|
|
47
|
-
function insertMemory(db, id, content) {
|
|
48
|
-
db.prepare(`INSERT INTO memories (id, tenant_id, user_id, content, trust_level, memory_type, source_who, tags, created_at)
|
|
49
|
-
VALUES (?, 'default', 'u1', ?, 'learned', 'semantic', 'agent', '[]', ?)`).run(id, content, Date.now());
|
|
50
|
-
}
|
|
51
|
-
describe("createEmbeddingQueue", () => {
|
|
52
|
-
let db;
|
|
53
|
-
beforeEach(() => {
|
|
54
|
-
db = new Database(":memory:");
|
|
55
|
-
initSchema(db, DIMS);
|
|
56
|
-
});
|
|
57
|
-
it("enqueue does not block (returns immediately before embedding completes)", async () => {
|
|
58
|
-
const port = createMockEmbeddingPort({ delay: 50 });
|
|
59
|
-
const queue = createEmbeddingQueue(db, port);
|
|
60
|
-
insertMemory(db, "m1", "hello world");
|
|
61
|
-
queue.enqueue("m1", "hello world");
|
|
62
|
-
// The call should have returned immediately -- check that the embedding
|
|
63
|
-
// is NOT yet stored (still processing in background)
|
|
64
|
-
const row = db.prepare("SELECT has_embedding FROM memories WHERE id = 'm1'").get();
|
|
65
|
-
expect(row.has_embedding).toBe(0);
|
|
66
|
-
// Now wait for completion
|
|
67
|
-
await queue.onIdle();
|
|
68
|
-
const after = db.prepare("SELECT has_embedding FROM memories WHERE id = 'm1'").get();
|
|
69
|
-
expect(after.has_embedding).toBe(1);
|
|
70
|
-
});
|
|
71
|
-
it("after onIdle(), entry has embedding in vec_memories", async () => {
|
|
72
|
-
if (!isVecAvailable())
|
|
73
|
-
return;
|
|
74
|
-
const port = createMockEmbeddingPort();
|
|
75
|
-
const queue = createEmbeddingQueue(db, port);
|
|
76
|
-
insertMemory(db, "m1", "test content");
|
|
77
|
-
queue.enqueue("m1", "test content");
|
|
78
|
-
await queue.onIdle();
|
|
79
|
-
const vecRow = db.prepare("SELECT memory_id FROM vec_memories WHERE memory_id = 'm1'").get();
|
|
80
|
-
expect(vecRow).toBeDefined();
|
|
81
|
-
expect(vecRow.memory_id).toBe("m1");
|
|
82
|
-
});
|
|
83
|
-
it("after onIdle(), memories.has_embedding = 1", async () => {
|
|
84
|
-
const port = createMockEmbeddingPort();
|
|
85
|
-
const queue = createEmbeddingQueue(db, port);
|
|
86
|
-
insertMemory(db, "m1", "test content");
|
|
87
|
-
queue.enqueue("m1", "test content");
|
|
88
|
-
await queue.onIdle();
|
|
89
|
-
const row = db.prepare("SELECT has_embedding FROM memories WHERE id = 'm1'").get();
|
|
90
|
-
expect(row.has_embedding).toBe(1);
|
|
91
|
-
});
|
|
92
|
-
it("multiple enqueues process FIFO (call order matches enqueue order)", async () => {
|
|
93
|
-
const port = createMockEmbeddingPort();
|
|
94
|
-
const queue = createEmbeddingQueue(db, port);
|
|
95
|
-
insertMemory(db, "m1", "first");
|
|
96
|
-
insertMemory(db, "m2", "second");
|
|
97
|
-
insertMemory(db, "m3", "third");
|
|
98
|
-
queue.enqueue("m1", "first");
|
|
99
|
-
queue.enqueue("m2", "second");
|
|
100
|
-
queue.enqueue("m3", "third");
|
|
101
|
-
await queue.onIdle();
|
|
102
|
-
expect(port.calls).toEqual(["first", "second", "third"]);
|
|
103
|
-
});
|
|
104
|
-
it("concurrency=1: second embed does not start until first finishes", async () => {
|
|
105
|
-
const timeline = [];
|
|
106
|
-
const port = {
|
|
107
|
-
provider: "test",
|
|
108
|
-
dimensions: DIMS,
|
|
109
|
-
modelId: "test-embed-model",
|
|
110
|
-
calls: [],
|
|
111
|
-
async embed(text) {
|
|
112
|
-
this.calls.push(text);
|
|
113
|
-
timeline.push({ text, event: "start" });
|
|
114
|
-
await new Promise((r) => setTimeout(r, 30));
|
|
115
|
-
timeline.push({ text, event: "end" });
|
|
116
|
-
return ok(new Array(DIMS).fill(0.5));
|
|
117
|
-
},
|
|
118
|
-
async embedBatch(texts) {
|
|
119
|
-
return ok(texts.map(() => new Array(DIMS).fill(0.5)));
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
const queue = createEmbeddingQueue(db, port);
|
|
123
|
-
insertMemory(db, "m1", "alpha");
|
|
124
|
-
insertMemory(db, "m2", "beta");
|
|
125
|
-
queue.enqueue("m1", "alpha");
|
|
126
|
-
queue.enqueue("m2", "beta");
|
|
127
|
-
await queue.onIdle();
|
|
128
|
-
// With concurrency=1, "alpha" must end before "beta" starts
|
|
129
|
-
const alphaEnd = timeline.findIndex((e) => e.text === "alpha" && e.event === "end");
|
|
130
|
-
const betaStart = timeline.findIndex((e) => e.text === "beta" && e.event === "start");
|
|
131
|
-
expect(alphaEnd).toBeLessThan(betaStart);
|
|
132
|
-
});
|
|
133
|
-
it("failed embedding does not crash queue (entry stays without embedding)", async () => {
|
|
134
|
-
const port = createMockEmbeddingPort({ fail: true });
|
|
135
|
-
const queue = createEmbeddingQueue(db, port);
|
|
136
|
-
insertMemory(db, "m1", "will fail");
|
|
137
|
-
queue.enqueue("m1", "will fail");
|
|
138
|
-
// Should not throw
|
|
139
|
-
await queue.onIdle();
|
|
140
|
-
// Entry should still exist but without embedding
|
|
141
|
-
const row = db.prepare("SELECT has_embedding FROM memories WHERE id = 'm1'").get();
|
|
142
|
-
expect(row.has_embedding).toBe(0);
|
|
143
|
-
// Memory itself is intact
|
|
144
|
-
const mem = db.prepare("SELECT content FROM memories WHERE id = 'm1'").get();
|
|
145
|
-
expect(mem.content).toBe("will fail");
|
|
146
|
-
});
|
|
147
|
-
it("failed embedding logs warning via structured logger", async () => {
|
|
148
|
-
const mockLogger = { warn: vi.fn() };
|
|
149
|
-
const port = createMockEmbeddingPort({ fail: true });
|
|
150
|
-
const queue = createEmbeddingQueue(db, port, mockLogger);
|
|
151
|
-
insertMemory(db, "m1", "will fail");
|
|
152
|
-
queue.enqueue("m1", "will fail");
|
|
153
|
-
await queue.onIdle();
|
|
154
|
-
expect(mockLogger.warn).toHaveBeenCalledWith(expect.objectContaining({
|
|
155
|
-
entryId: "m1",
|
|
156
|
-
err: expect.stringContaining("mock embedding failure"),
|
|
157
|
-
hint: expect.stringContaining("FTS5"),
|
|
158
|
-
errorKind: "dependency",
|
|
159
|
-
}), "Embedding generation failed");
|
|
160
|
-
});
|
|
161
|
-
it("pending() returns correct count before and after processing", async () => {
|
|
162
|
-
const port = createMockEmbeddingPort({ delay: 30 });
|
|
163
|
-
const queue = createEmbeddingQueue(db, port);
|
|
164
|
-
// Before any enqueue
|
|
165
|
-
expect(queue.pending()).toBe(0);
|
|
166
|
-
insertMemory(db, "m1", "a");
|
|
167
|
-
insertMemory(db, "m2", "b");
|
|
168
|
-
insertMemory(db, "m3", "c");
|
|
169
|
-
queue.enqueue("m1", "a");
|
|
170
|
-
queue.enqueue("m2", "b");
|
|
171
|
-
queue.enqueue("m3", "c");
|
|
172
|
-
// At least some should be pending (1 running + others queued)
|
|
173
|
-
expect(queue.pending()).toBeGreaterThan(0);
|
|
174
|
-
await queue.onIdle();
|
|
175
|
-
expect(queue.pending()).toBe(0);
|
|
176
|
-
});
|
|
177
|
-
it("pause() stops processing, resume() continues", async () => {
|
|
178
|
-
const port = createMockEmbeddingPort({ delay: 10 });
|
|
179
|
-
const queue = createEmbeddingQueue(db, port);
|
|
180
|
-
insertMemory(db, "m1", "a");
|
|
181
|
-
insertMemory(db, "m2", "b");
|
|
182
|
-
queue.pause();
|
|
183
|
-
queue.enqueue("m1", "a");
|
|
184
|
-
queue.enqueue("m2", "b");
|
|
185
|
-
// Give a tick for anything that might process
|
|
186
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
187
|
-
// Nothing should have been processed while paused
|
|
188
|
-
expect(port.calls).toHaveLength(0);
|
|
189
|
-
queue.resume();
|
|
190
|
-
await queue.onIdle();
|
|
191
|
-
expect(port.calls).toHaveLength(2);
|
|
192
|
-
});
|
|
193
|
-
it("clear() removes pending items", async () => {
|
|
194
|
-
const port = createMockEmbeddingPort({ delay: 50 });
|
|
195
|
-
const queue = createEmbeddingQueue(db, port);
|
|
196
|
-
insertMemory(db, "m1", "a");
|
|
197
|
-
insertMemory(db, "m2", "b");
|
|
198
|
-
insertMemory(db, "m3", "c");
|
|
199
|
-
queue.enqueue("m1", "a");
|
|
200
|
-
queue.enqueue("m2", "b");
|
|
201
|
-
queue.enqueue("m3", "c");
|
|
202
|
-
// Clear pending (in-progress item may still complete)
|
|
203
|
-
queue.clear();
|
|
204
|
-
await queue.onIdle();
|
|
205
|
-
// Only the first item (already in-progress when clear was called) should have been processed
|
|
206
|
-
// or possibly none if clear was very fast. At most 1 should complete.
|
|
207
|
-
expect(port.calls.length).toBeLessThanOrEqual(1);
|
|
208
|
-
});
|
|
209
|
-
it("onIdle() resolves immediately when queue is empty", async () => {
|
|
210
|
-
const port = createMockEmbeddingPort();
|
|
211
|
-
const queue = createEmbeddingQueue(db, port);
|
|
212
|
-
const start = Date.now();
|
|
213
|
-
await queue.onIdle();
|
|
214
|
-
const elapsed = Date.now() - start;
|
|
215
|
-
// Should resolve nearly instantly (under 50ms)
|
|
216
|
-
expect(elapsed).toBeLessThan(50);
|
|
217
|
-
});
|
|
218
|
-
it("embedding is stored as Float32Array in vec_memories", async () => {
|
|
219
|
-
if (!isVecAvailable())
|
|
220
|
-
return;
|
|
221
|
-
const port = createMockEmbeddingPort();
|
|
222
|
-
const queue = createEmbeddingQueue(db, port);
|
|
223
|
-
insertMemory(db, "m1", "AB");
|
|
224
|
-
queue.enqueue("m1", "AB");
|
|
225
|
-
await queue.onIdle();
|
|
226
|
-
// Query the raw embedding from vec_memories
|
|
227
|
-
const vecRow = db.prepare("SELECT embedding FROM vec_memories WHERE memory_id = 'm1'").get();
|
|
228
|
-
expect(vecRow).toBeDefined();
|
|
229
|
-
// Convert Buffer back to Float32Array and verify values
|
|
230
|
-
const float32 = new Float32Array(vecRow.embedding.buffer, vecRow.embedding.byteOffset, vecRow.embedding.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
|
231
|
-
expect(float32.length).toBe(DIMS);
|
|
232
|
-
// "A" = 65, "B" = 66; charCode / 256
|
|
233
|
-
expect(float32[0]).toBeCloseTo(65 / 256, 5);
|
|
234
|
-
expect(float32[1]).toBeCloseTo(66 / 256, 5);
|
|
235
|
-
});
|
|
236
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|